/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 and
 * only version 2 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

#define pr_fmt(fmt) "AXI: BIMC: %s(): " fmt, __func__

#include <linux/slab.h>
#include <linux/io.h>
#include <linux/msm-bus-board.h>
#include "msm_bus_core.h"
#include "msm_bus_bimc.h"
#include "msm_bus_adhoc.h"
#include <trace/events/trace_msm_bus.h>

enum msm_bus_bimc_slave_block {
	SLAVE_BLOCK_RESERVED = 0,
	SLAVE_BLOCK_SLAVE_WAY,
	SLAVE_BLOCK_XPU,
	SLAVE_BLOCK_ARBITER,
	SLAVE_BLOCK_SCMO,
};

enum bke_sw {
	BKE_OFF = 0,
	BKE_ON = 1,
};

/* M_Generic */

#define M_REG_BASE(b)		((b) + 0x00008000)

#define M_COMPONENT_INFO_ADDR(b, n) \
		(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000000)
enum bimc_m_component_info {
	M_COMPONENT_INFO_RMSK		= 0xffffff,
	M_COMPONENT_INFO_INSTANCE_BMSK	= 0xff0000,
	M_COMPONENT_INFO_INSTANCE_SHFT	= 0x10,
	M_COMPONENT_INFO_SUB_TYPE_BMSK	= 0xff00,
	M_COMPONENT_INFO_SUB_TYPE_SHFT	= 0x8,
	M_COMPONENT_INFO_TYPE_BMSK	= 0xff,
	M_COMPONENT_INFO_TYPE_SHFT	= 0x0,
};

#define M_CONFIG_INFO_0_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000020)
enum bimc_m_config_info_0 {
	M_CONFIG_INFO_0_RMSK			= 0xff00ffff,
	M_CONFIG_INFO_0_SYNC_MODE_BMSK		= 0xff000000,
	M_CONFIG_INFO_0_SYNC_MODE_SHFT		= 0x18,
	M_CONFIG_INFO_0_CONNECTION_TYPE_BMSK	= 0xff00,
	M_CONFIG_INFO_0_CONNECTION_TYPE_SHFT	= 0x8,
	M_CONFIG_INFO_0_FUNC_BMSK		= 0xff,
	M_CONFIG_INFO_0_FUNC_SHFT		= 0x0,
};

#define M_CONFIG_INFO_1_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000030)
enum bimc_m_config_info_1 {
	M_CONFIG_INFO_1_RMSK			= 0xffffffff,
	M_CONFIG_INFO_1_SWAY_CONNECTIVITY_BMSK	= 0xffffffff,
	M_CONFIG_INFO_1_SWAY_CONNECTIVITY_SHFT	= 0x0,
};

#define M_CONFIG_INFO_2_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000040)
enum bimc_m_config_info_2 {
	M_CONFIG_INFO_2_RMSK			= 0xffffffff,
	M_CONFIG_INFO_2_M_DATA_WIDTH_BMSK	= 0xffff0000,
	M_CONFIG_INFO_2_M_DATA_WIDTH_SHFT	= 0x10,
	M_CONFIG_INFO_2_M_TID_WIDTH_BMSK	= 0xff00,
	M_CONFIG_INFO_2_M_TID_WIDTH_SHFT	= 0x8,
	M_CONFIG_INFO_2_M_MID_WIDTH_BMSK	= 0xff,
	M_CONFIG_INFO_2_M_MID_WIDTH_SHFT	= 0x0,
};

#define M_CONFIG_INFO_3_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000050)
enum bimc_m_config_info_3 {
	M_CONFIG_INFO_3_RMSK			= 0xffffffff,
	M_CONFIG_INFO_3_RCH_DEPTH_BMSK		= 0xff000000,
	M_CONFIG_INFO_3_RCH_DEPTH_SHFT		= 0x18,
	M_CONFIG_INFO_3_BCH_DEPTH_BMSK		= 0xff0000,
	M_CONFIG_INFO_3_BCH_DEPTH_SHFT		= 0x10,
	M_CONFIG_INFO_3_WCH_DEPTH_BMSK		= 0xff00,
	M_CONFIG_INFO_3_WCH_DEPTH_SHFT		= 0x8,
	M_CONFIG_INFO_3_ACH_DEPTH_BMSK		= 0xff,
	M_CONFIG_INFO_3_ACH_DEPTH_SHFT		= 0x0,
};

#define M_CONFIG_INFO_4_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000060)
enum bimc_m_config_info_4 {
	M_CONFIG_INFO_4_RMSK			= 0xffff,
	M_CONFIG_INFO_4_REORDER_BUF_DEPTH_BMSK	= 0xff00,
	M_CONFIG_INFO_4_REORDER_BUF_DEPTH_SHFT	= 0x8,
	M_CONFIG_INFO_4_REORDER_TABLE_DEPTH_BMSK	= 0xff,
	M_CONFIG_INFO_4_REORDER_TABLE_DEPTH_SHFT	= 0x0,
};

#define M_CONFIG_INFO_5_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000070)
enum bimc_m_config_info_5 {
	M_CONFIG_INFO_5_RMSK			= 0x111,
	M_CONFIG_INFO_5_MP2ARB_PIPELINE_EN_BMSK	= 0x100,
	M_CONFIG_INFO_5_MP2ARB_PIPELINE_EN_SHFT	= 0x8,
	M_CONFIG_INFO_5_MPBUF_PIPELINE_EN_BMSK	= 0x10,
	M_CONFIG_INFO_5_MPBUF_PIPELINE_EN_SHFT	= 0x4,
	M_CONFIG_INFO_5_M2MP_PIPELINE_EN_BMSK	= 0x1,
	M_CONFIG_INFO_5_M2MP_PIPELINE_EN_SHFT	= 0x0,
};

#define M_INT_STATUS_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000100)
enum bimc_m_int_status {
	M_INT_STATUS_RMSK			= 0x3,
};

#define M_INT_CLR_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000108)
enum bimc_m_int_clr {
	M_INT_CLR_RMSK			= 0x3,
};

#define M_INT_EN_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x0000010c)
enum bimc_m_int_en {
	M_INT_EN_RMSK			= 0x3,
};

#define M_CLK_CTRL_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000200)
enum bimc_m_clk_ctrl {
	M_CLK_CTRL_RMSK				= 0x3,
	M_CLK_CTRL_MAS_CLK_GATING_EN_BMSK	= 0x2,
	M_CLK_CTRL_MAS_CLK_GATING_EN_SHFT	= 0x1,
	M_CLK_CTRL_CORE_CLK_GATING_EN_BMSK	= 0x1,
	M_CLK_CTRL_CORE_CLK_GATING_EN_SHFT	= 0x0,
};

#define M_MODE_ADDR(b, n) \
		(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000210)
enum bimc_m_mode {
	M_MODE_RMSK				= 0xf0000011,
	M_MODE_WR_GATHER_BEATS_BMSK		= 0xf0000000,
	M_MODE_WR_GATHER_BEATS_SHFT		= 0x1c,
	M_MODE_NARROW_WR_BMSK			= 0x10,
	M_MODE_NARROW_WR_SHFT			= 0x4,
	M_MODE_ORDERING_MODEL_BMSK		= 0x1,
	M_MODE_ORDERING_MODEL_SHFT		= 0x0,
};

#define M_PRIOLVL_OVERRIDE_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000230)
enum bimc_m_priolvl_override {
	M_PRIOLVL_OVERRIDE_RMSK			= 0x301,
	M_PRIOLVL_OVERRIDE_BMSK			= 0x300,
	M_PRIOLVL_OVERRIDE_SHFT			= 0x8,
	M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK	= 0x1,
	M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_SHFT	= 0x0,
};

#define M_RD_CMD_OVERRIDE_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000240)
enum bimc_m_read_command_override {
	M_RD_CMD_OVERRIDE_RMSK			= 0x3071f7f,
	M_RD_CMD_OVERRIDE_AREQPRIO_BMSK		= 0x3000000,
	M_RD_CMD_OVERRIDE_AREQPRIO_SHFT		= 0x18,
	M_RD_CMD_OVERRIDE_AMEMTYPE_BMSK		= 0x70000,
	M_RD_CMD_OVERRIDE_AMEMTYPE_SHFT		= 0x10,
	M_RD_CMD_OVERRIDE_ATRANSIENT_BMSK		= 0x1000,
	M_RD_CMD_OVERRIDE_ATRANSIENT_SHFT		= 0xc,
	M_RD_CMD_OVERRIDE_ASHARED_BMSK		= 0x800,
	M_RD_CMD_OVERRIDE_ASHARED_SHFT		= 0xb,
	M_RD_CMD_OVERRIDE_AREDIRECT_BMSK		= 0x400,
	M_RD_CMD_OVERRIDE_AREDIRECT_SHFT		= 0xa,
	M_RD_CMD_OVERRIDE_AOOO_BMSK			= 0x200,
	M_RD_CMD_OVERRIDE_AOOO_SHFT			= 0x9,
	M_RD_CMD_OVERRIDE_AINNERSHARED_BMSK		= 0x100,
	M_RD_CMD_OVERRIDE_AINNERSHARED_SHFT		= 0x8,
	M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK	= 0x40,
	M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT	= 0x6,
	M_RD_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_BMSK	= 0x20,
	M_RD_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_SHFT	= 0x5,
	M_RD_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_BMSK	= 0x10,
	M_RD_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_SHFT	= 0x4,
	M_RD_CMD_OVERRIDE_OVERRIDE_ASHARED_BMSK	= 0x8,
	M_RD_CMD_OVERRIDE_OVERRIDE_ASHARED_SHFT	= 0x3,
	M_RD_CMD_OVERRIDE_OVERRIDE_AREDIRECT_BMSK	= 0x4,
	M_RD_CMD_OVERRIDE_OVERRIDE_AREDIRECT_SHFT	= 0x2,
	M_RD_CMD_OVERRIDE_OVERRIDE_AOOO_BMSK		= 0x2,
	M_RD_CMD_OVERRIDE_OVERRIDE_AOOO_SHFT		= 0x1,
	M_RD_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_BMSK	= 0x1,
	M_RD_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_SHFT	= 0x0,
};

#define M_WR_CMD_OVERRIDE_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000250)
enum bimc_m_write_command_override {
	M_WR_CMD_OVERRIDE_RMSK			= 0x3071f7f,
	M_WR_CMD_OVERRIDE_AREQPRIO_BMSK		= 0x3000000,
	M_WR_CMD_OVERRIDE_AREQPRIO_SHFT		= 0x18,
	M_WR_CMD_OVERRIDE_AMEMTYPE_BMSK		= 0x70000,
	M_WR_CMD_OVERRIDE_AMEMTYPE_SHFT		= 0x10,
	M_WR_CMD_OVERRIDE_ATRANSIENT_BMSK	= 0x1000,
	M_WR_CMD_OVERRIDE_ATRANSIENT_SHFT	= 0xc,
	M_WR_CMD_OVERRIDE_ASHARED_BMSK		= 0x800,
	M_WR_CMD_OVERRIDE_ASHARED_SHFT		= 0xb,
	M_WR_CMD_OVERRIDE_AREDIRECT_BMSK		= 0x400,
	M_WR_CMD_OVERRIDE_AREDIRECT_SHFT		= 0xa,
	M_WR_CMD_OVERRIDE_AOOO_BMSK			= 0x200,
	M_WR_CMD_OVERRIDE_AOOO_SHFT			= 0x9,
	M_WR_CMD_OVERRIDE_AINNERSHARED_BMSK		= 0x100,
	M_WR_CMD_OVERRIDE_AINNERSHARED_SHFT		= 0x8,
	M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK	= 0x40,
	M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT	= 0x6,
	M_WR_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_BMSK	= 0x20,
	M_WR_CMD_OVERRIDE_OVERRIDE_ATRANSIENT_SHFT	= 0x5,
	M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_BMSK	= 0x10,
	M_WR_CMD_OVERRIDE_OVERRIDE_AMEMTYPE_SHFT	= 0x4,
	M_WR_CMD_OVERRIDE_OVERRIDE_ASHARED_BMSK	= 0x8,
	M_WR_CMD_OVERRIDE_OVERRIDE_ASHARED_SHFT	= 0x3,
	M_WR_CMD_OVERRIDE_OVERRIDE_AREDIRECT_BMSK	= 0x4,
	M_WR_CMD_OVERRIDE_OVERRIDE_AREDIRECT_SHFT	= 0x2,
	M_WR_CMD_OVERRIDE_OVERRIDE_AOOO_BMSK	= 0x2,
	M_WR_CMD_OVERRIDE_OVERRIDE_AOOO_SHFT	= 0x1,
	M_WR_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_BMSK	= 0x1,
	M_WR_CMD_OVERRIDE_OVERRIDE_AINNERSHARED_SHFT	= 0x0,
};

#define M_BKE_EN_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000300)
enum bimc_m_bke_en {
	M_BKE_EN_RMSK			= 0x1,
	M_BKE_EN_EN_BMSK		= 0x1,
	M_BKE_EN_EN_SHFT		= 0x0,
};

/* Grant Period registers */
#define M_BKE_GP_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000304)
enum bimc_m_bke_grant_period {
	M_BKE_GP_RMSK		= 0x3ff,
	M_BKE_GP_GP_BMSK	= 0x3ff,
	M_BKE_GP_GP_SHFT	= 0x0,
};

/* Grant count register.
 * The Grant count register represents a signed 16 bit
 * value, range 0-0x7fff
 */
#define M_BKE_GC_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000308)
enum bimc_m_bke_grant_count {
	M_BKE_GC_RMSK			= 0xffff,
	M_BKE_GC_GC_BMSK		= 0xffff,
	M_BKE_GC_GC_SHFT		= 0x0,
};

/* Threshold High Registers */
#define M_BKE_THH_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000320)
enum bimc_m_bke_thresh_high {
	M_BKE_THH_RMSK		= 0xffff,
	M_BKE_THH_THRESH_BMSK	= 0xffff,
	M_BKE_THH_THRESH_SHFT	= 0x0,
};

/* Threshold Medium Registers */
#define M_BKE_THM_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000324)
enum bimc_m_bke_thresh_medium {
	M_BKE_THM_RMSK		= 0xffff,
	M_BKE_THM_THRESH_BMSK	= 0xffff,
	M_BKE_THM_THRESH_SHFT	= 0x0,
};

/* Threshold Low Registers */
#define M_BKE_THL_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000328)
enum bimc_m_bke_thresh_low {
	M_BKE_THL_RMSK			= 0xffff,
	M_BKE_THL_THRESH_BMSK		= 0xffff,
	M_BKE_THL_THRESH_SHFT		= 0x0,
};

#define M_BKE_HEALTH_0_CONFIG_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000340)
enum bimc_m_bke_health_0 {
	M_BKE_HEALTH_0_CONFIG_RMSK			= 0x80000303,
	M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_BMSK		= 0x80000000,
	M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_SHFT		= 0x1f,
	M_BKE_HEALTH_0_CONFIG_AREQPRIO_BMSK		= 0x300,
	M_BKE_HEALTH_0_CONFIG_AREQPRIO_SHFT		= 0x8,
	M_BKE_HEALTH_0_CONFIG_PRIOLVL_BMSK		= 0x3,
	M_BKE_HEALTH_0_CONFIG_PRIOLVL_SHFT		= 0x0,
};

#define M_BKE_HEALTH_1_CONFIG_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000344)
enum bimc_m_bke_health_1 {
	M_BKE_HEALTH_1_CONFIG_RMSK			= 0x80000303,
	M_BKE_HEALTH_1_CONFIG_LIMIT_CMDS_BMSK		= 0x80000000,
	M_BKE_HEALTH_1_CONFIG_LIMIT_CMDS_SHFT		= 0x1f,
	M_BKE_HEALTH_1_CONFIG_AREQPRIO_BMSK		= 0x300,
	M_BKE_HEALTH_1_CONFIG_AREQPRIO_SHFT		= 0x8,
	M_BKE_HEALTH_1_CONFIG_PRIOLVL_BMSK		= 0x3,
	M_BKE_HEALTH_1_CONFIG_PRIOLVL_SHFT		= 0x0,
};

#define M_BKE_HEALTH_2_CONFIG_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000348)
enum bimc_m_bke_health_2 {
	M_BKE_HEALTH_2_CONFIG_RMSK			= 0x80000303,
	M_BKE_HEALTH_2_CONFIG_LIMIT_CMDS_BMSK		= 0x80000000,
	M_BKE_HEALTH_2_CONFIG_LIMIT_CMDS_SHFT		= 0x1f,
	M_BKE_HEALTH_2_CONFIG_AREQPRIO_BMSK		= 0x300,
	M_BKE_HEALTH_2_CONFIG_AREQPRIO_SHFT		= 0x8,
	M_BKE_HEALTH_2_CONFIG_PRIOLVL_BMSK		= 0x3,
	M_BKE_HEALTH_2_CONFIG_PRIOLVL_SHFT		= 0x0,
};

#define M_BKE_HEALTH_3_CONFIG_ADDR(b, n) \
	(M_REG_BASE(b) + (0x4000 * (n)) + 0x0000034c)
enum bimc_m_bke_health_3 {
	M_BKE_HEALTH_3_CONFIG_RMSK			= 0x303,
	M_BKE_HEALTH_3_CONFIG_AREQPRIO_BMSK	= 0x300,
	M_BKE_HEALTH_3_CONFIG_AREQPRIO_SHFT	= 0x8,
	M_BKE_HEALTH_3_CONFIG_PRIOLVL_BMSK		= 0x3,
	M_BKE_HEALTH_3_CONFIG_PRIOLVL_SHFT		= 0x0,
};

#define M_BUF_STATUS_ADDR(b, n) \
		(M_REG_BASE(b) + (0x4000 * (n)) + 0x00000400)
enum bimc_m_buf_status {
	M_BUF_STATUS_RMSK			= 0xf03f030,
	M_BUF_STATUS_RCH_DATA_WR_FULL_BMSK	= 0x8000000,
	M_BUF_STATUS_RCH_DATA_WR_FULL_SHFT	= 0x1b,
	M_BUF_STATUS_RCH_DATA_WR_EMPTY_BMSK	= 0x4000000,
	M_BUF_STATUS_RCH_DATA_WR_EMPTY_SHFT	= 0x1a,
	M_BUF_STATUS_RCH_CTRL_WR_FULL_BMSK	= 0x2000000,
	M_BUF_STATUS_RCH_CTRL_WR_FULL_SHFT	= 0x19,
	M_BUF_STATUS_RCH_CTRL_WR_EMPTY_BMSK	= 0x1000000,
	M_BUF_STATUS_RCH_CTRL_WR_EMPTY_SHFT	= 0x18,
	M_BUF_STATUS_BCH_WR_FULL_BMSK		= 0x20000,
	M_BUF_STATUS_BCH_WR_FULL_SHFT		= 0x11,
	M_BUF_STATUS_BCH_WR_EMPTY_BMSK		= 0x10000,
	M_BUF_STATUS_BCH_WR_EMPTY_SHFT		= 0x10,
	M_BUF_STATUS_WCH_DATA_RD_FULL_BMSK	= 0x8000,
	M_BUF_STATUS_WCH_DATA_RD_FULL_SHFT	= 0xf,
	M_BUF_STATUS_WCH_DATA_RD_EMPTY_BMSK	= 0x4000,
	M_BUF_STATUS_WCH_DATA_RD_EMPTY_SHFT	= 0xe,
	M_BUF_STATUS_WCH_CTRL_RD_FULL_BMSK	= 0x2000,
	M_BUF_STATUS_WCH_CTRL_RD_FULL_SHFT	= 0xd,
	M_BUF_STATUS_WCH_CTRL_RD_EMPTY_BMSK	= 0x1000,
	M_BUF_STATUS_WCH_CTRL_RD_EMPTY_SHFT	= 0xc,
	M_BUF_STATUS_ACH_RD_FULL_BMSK		= 0x20,
	M_BUF_STATUS_ACH_RD_FULL_SHFT		= 0x5,
	M_BUF_STATUS_ACH_RD_EMPTY_BMSK		= 0x10,
	M_BUF_STATUS_ACH_RD_EMPTY_SHFT		= 0x4,
};
/*BIMC Generic */

#define S_REG_BASE(b)	((b) + 0x00048000)

#define S_COMPONENT_INFO_ADDR(b, n) \
	(S_REG_BASE(b) + (0x8000 * (n)) + 0x00000000)
enum bimc_s_component_info {
	S_COMPONENT_INFO_RMSK			= 0xffffff,
	S_COMPONENT_INFO_INSTANCE_BMSK		= 0xff0000,
	S_COMPONENT_INFO_INSTANCE_SHFT		= 0x10,
	S_COMPONENT_INFO_SUB_TYPE_BMSK		= 0xff00,
	S_COMPONENT_INFO_SUB_TYPE_SHFT		= 0x8,
	S_COMPONENT_INFO_TYPE_BMSK		= 0xff,
	S_COMPONENT_INFO_TYPE_SHFT		= 0x0,
};

#define S_HW_INFO_ADDR(b, n) \
	(S_REG_BASE(b) + (0x80000 * (n)) + 0x00000010)
enum bimc_s_hw_info {
	S_HW_INFO_RMSK				= 0xffffffff,
	S_HW_INFO_MAJOR_BMSK			= 0xff000000,
	S_HW_INFO_MAJOR_SHFT			= 0x18,
	S_HW_INFO_BRANCH_BMSK			= 0xff0000,
	S_HW_INFO_BRANCH_SHFT			= 0x10,
	S_HW_INFO_MINOR_BMSK			= 0xff00,
	S_HW_INFO_MINOR_SHFT			= 0x8,
	S_HW_INFO_ECO_BMSK			= 0xff,
	S_HW_INFO_ECO_SHFT			= 0x0,
};


/* S_SCMO_GENERIC */

#define S_SCMO_REG_BASE(b)	((b) + 0x00048000)

#define S_SCMO_CONFIG_INFO_0_ADDR(b, n) \
		(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000020)
enum bimc_s_scmo_config_info_0 {
	S_SCMO_CONFIG_INFO_0_RMSK		= 0xffffffff,
	S_SCMO_CONFIG_INFO_0_DATA_WIDTH_BMSK	= 0xffff0000,
	S_SCMO_CONFIG_INFO_0_DATA_WIDTH_SHFT	= 0x10,
	S_SCMO_CONFIG_INFO_0_TID_WIDTH_BMSK	= 0xff00,
	S_SCMO_CONFIG_INFO_0_TID_WIDTH_SHFT	= 0x8,
	S_SCMO_CONFIG_INFO_0_MID_WIDTH_BMSK	= 0xff,
	S_SCMO_CONFIG_INFO_0_MID_WIDTH_SHFT	= 0x0,
};

#define S_SCMO_CONFIG_INFO_1_ADDR(b, n) \
		(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000030)
enum bimc_s_scmo_config_info_1 {
	S_SCMO_CONFIG_INFO_1_RMSK			= 0xffffffff,
	S_SCMO_CONFIG_INFO_1_MPORT_CONNECTIVITY_BMSK	= 0xffffffff,
	S_SCMO_CONFIG_INFO_1_MPORT_CONNECTIVITY_SHFT	= 0x0,
};

#define S_SCMO_CONFIG_INFO_2_ADDR(b, n) \
		(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000040)
enum bimc_s_scmo_config_info_2 {
	S_SCMO_CONFIG_INFO_2_RMSK			= 0xff00ff,
	S_SCMO_CONFIG_INFO_2_NUM_GLOBAL_MONS_BMSK	= 0xff0000,
	S_SCMO_CONFIG_INFO_2_NUM_GLOBAL_MONS_SHFT	= 0x10,
	S_SCMO_CONFIG_INFO_2_VMID_WIDTH_BMSK	= 0xff,
	S_SCMO_CONFIG_INFO_2_VMID_WIDTH_SHFT	= 0x0,
};

#define S_SCMO_CONFIG_INFO_3_ADDR(b, n) \
		(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000050)
enum bimc_s_scmo_config_info_3 {
	S_SCMO_CONFIG_INFO_3_RMSK			= 0xffffffff,
	S_SCMO_CONFIG_INFO_3_RCH0_CTRL_DEPTH_BMSK	= 0xff000000,
	S_SCMO_CONFIG_INFO_3_RCH0_CTRL_DEPTH_SHFT	= 0x18,
	S_SCMO_CONFIG_INFO_3_RCH0_DEPTH_BMSK		= 0xff0000,
	S_SCMO_CONFIG_INFO_3_RCH0_DEPTH_SHFT		= 0x10,
	S_SCMO_CONFIG_INFO_3_BCH_DEPTH_BMSK		= 0xff00,
	S_SCMO_CONFIG_INFO_3_BCH_DEPTH_SHFT		= 0x8,
	S_SCMO_CONFIG_INFO_3_WCH_DEPTH_BMSK		= 0xff,
	S_SCMO_CONFIG_INFO_3_WCH_DEPTH_SHFT		= 0x0,
};

#define S_SCMO_CONFIG_INFO_4_ADDR(b, n) \
		(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000060)
enum bimc_s_scmo_config_info_4 {
	S_SCMO_CONFIG_INFO_4_RMSK			= 0xffff,
	S_SCMO_CONFIG_INFO_4_RCH1_CTRL_DEPTH_BMSK	= 0xff00,
	S_SCMO_CONFIG_INFO_4_RCH1_CTRL_DEPTH_SHFT	= 0x8,
	S_SCMO_CONFIG_INFO_4_RCH1_DEPTH_BMSK		= 0xff,
	S_SCMO_CONFIG_INFO_4_RCH1_DEPTH_SHFT		= 0x0,
};

#define S_SCMO_CONFIG_INFO_5_ADDR(b, n) \
		(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000070)
enum bimc_s_scmo_config_info_5 {
	S_SCMO_CONFIG_INFO_5_RMSK			= 0xffff,
	S_SCMO_CONFIG_INFO_5_DPE_CQ_DEPTH_BMSK		= 0xff00,
	S_SCMO_CONFIG_INFO_5_DPE_CQ_DEPTH_SHFT		= 0x8,
	S_SCMO_CONFIG_INFO_5_DDR_BUS_WIDTH_BMSK		= 0xff,
	S_SCMO_CONFIG_INFO_5_DDR_BUS_WIDTH_SHFT		= 0x0,
};

#define S_SCMO_CONFIG_INFO_6_ADDR(b, n) \
		(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000080)
enum bimc_s_scmo_config_info_6 {
	S_SCMO_CONFIG_INFO_6_RMSK			= 0x1111,
	S_SCMO_CONFIG_INFO_6_WBUFC_PIPE_BMSK		= 0x1000,
	S_SCMO_CONFIG_INFO_6_WBUFC_PIPE_SHFT		= 0xc,
	S_SCMO_CONFIG_INFO_6_RDOPT_PIPE_BMSK		= 0x100,
	S_SCMO_CONFIG_INFO_6_RDOPT_PIPE_SHFT		= 0x8,
	S_SCMO_CONFIG_INFO_6_ACHAN_INTF_PIPE_BMSK	= 0x10,
	S_SCMO_CONFIG_INFO_6_ACHAN_INTF_PIPE_SHFT	= 0x4,
	S_SCMO_CONFIG_INFO_6_ADDR_DECODE_HT_BMSK	= 0x1,
	S_SCMO_CONFIG_INFO_6_ADDR_DECODE_HT_SHFT	= 0x0,
};

#define S_SCMO_INT_STATUS_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000100)
enum bimc_s_scmo_int_status {
	S_SCMO_INT_STATUS_RMSK			= 0x1,
	S_SCMO_INT_STATUS_ERR_OCCURED_BMSK	= 0x1,
	S_SCMO_INT_STATUS_ERR_OCCURED_SHFT	= 0x0,
};

#define S_SCMO_INT_CLR_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000108)
enum bimc_s_scmo_int_clr {
	S_SCMO_INT_CLR_RMSK		= 0x1,
	S_SCMO_INT_CLR_IRQ_CLR_BMSK	= 0x1,
	S_SCMO_INT_CLR_IRQ_CLR_SHFT	= 0x0,
};

#define S_SCMO_INT_EN_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x0000010c)
enum bimc_s_scmo_int_en {
	S_SCMO_INT_EN_RMSK		= 0x1,
	S_SCMO_INT_EN_IRQ_EN_BMSK	= 0x1,
	S_SCMO_INT_EN_IRQ_EN_SHFT	= 0x0,
};

#define S_SCMO_ESYN_ADDR_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000120)
enum bimc_s_scmo_esyn_addr {
	S_SCMO_ESYN_ADDR_RMSK				= 0xffffffff,
	S_SCMO_ESYN_ADDR_ESYN_ADDR_ERR_ADDR_BMSK	= 0xffffffff,
	S_SCMO_ESYN_ADDR_ESYN_ADDR_ERR_ADDR_SHFT	= 0x0,
};

#define S_SCMO_ESYN_APACKET_0_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000128)
enum bimc_s_scmo_esyn_apacket_0 {
	S_SCMO_ESYN_APACKET_0_RMSK			= 0xff1fffff,
	S_SCMO_ESYN_APACKET_0_ERR_ATID_BMSK		= 0xff000000,
	S_SCMO_ESYN_APACKET_0_ERR_ATID_SHFT		= 0x18,
	S_SCMO_ESYN_APACKET_0_ERR_AVMID_BMSK		= 0x1f0000,
	S_SCMO_ESYN_APACKET_0_ERR_AVMID_SHFT		= 0x10,
	S_SCMO_ESYN_APACKET_0_ERR_AMID_BMSK		= 0xffff,
	S_SCMO_ESYN_APACKET_0_ERR_AMID_SHFT		= 0x0,
};

#define S_SCMO_ESYN_APACKET_1_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x0000012c)
enum bimc_s_scmo_esyn_apacket_1 {
	S_SCMO_ESYN_APACKET_1_RMSK			= 0x10ff117,
	S_SCMO_ESYN_APACKET_1_ERR_CODE_BMSK		= 0x1000000,
	S_SCMO_ESYN_APACKET_1_ERR_CODE_SHFT		= 0x18,
	S_SCMO_ESYN_APACKET_1_ERR_ALEN_BMSK		= 0xf0000,
	S_SCMO_ESYN_APACKET_1_ERR_ALEN_SHFT		= 0x10,
	S_SCMO_ESYN_APACKET_1_ERR_ASIZE_BMSK		= 0xe000,
	S_SCMO_ESYN_APACKET_1_ERR_ASIZE_SHFT		= 0xd,
	S_SCMO_ESYN_APACKET_1_ERR_ABURST_BMSK		= 0x1000,
	S_SCMO_ESYN_APACKET_1_ERR_ABURST_SHFT		= 0xc,
	S_SCMO_ESYN_APACKET_1_ERR_AEXCLUSIVE_BMSK	= 0x100,
	S_SCMO_ESYN_APACKET_1_ERR_AEXCLUSIVE_SHFT	= 0x8,
	S_SCMO_ESYN_APACKET_1_ERR_APRONTS_BMSK		= 0x10,
	S_SCMO_ESYN_APACKET_1_ERR_APRONTS_SHFT		= 0x4,
	S_SCMO_ESYN_APACKET_1_ERR_AOOORD_BMSK		= 0x4,
	S_SCMO_ESYN_APACKET_1_ERR_AOOORD_SHFT		= 0x2,
	S_SCMO_ESYN_APACKET_1_ERR_AOOOWR_BMSK		= 0x2,
	S_SCMO_ESYN_APACKET_1_ERR_AOOOWR_SHFT		= 0x1,
	S_SCMO_ESYN_APACKET_1_ERR_AWRITE_BMSK		= 0x1,
	S_SCMO_ESYN_APACKET_1_ERR_AWRITE_SHFT		= 0x0,
};

#define S_SCMO_CLK_CTRL_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000200)
enum bimc_s_scmo_clk_ctrl {
	S_SCMO_CLK_CTRL_RMSK				= 0xffff1111,
	S_SCMO_CLK_CTRL_PEN_CMD_CG_EN_BMSK		= 0x10000,
	S_SCMO_CLK_CTRL_PEN_CMD_CG_EN_SHFT		= 0x10,
	S_SCMO_CLK_CTRL_RCH_CG_EN_BMSK			= 0x1000,
	S_SCMO_CLK_CTRL_RCH_CG_EN_SHFT			= 0xc,
	S_SCMO_CLK_CTRL_FLUSH_CG_EN_BMSK		= 0x100,
	S_SCMO_CLK_CTRL_FLUSH_CG_EN_SHFT		= 0x8,
	S_SCMO_CLK_CTRL_WCH_CG_EN_BMSK			= 0x10,
	S_SCMO_CLK_CTRL_WCH_CG_EN_SHFT			= 0x4,
	S_SCMO_CLK_CTRL_ACH_CG_EN_BMSK			= 0x1,
	S_SCMO_CLK_CTRL_ACH_CG_EN_SHFT			= 0x0,
};

#define S_SCMO_SLV_INTERLEAVE_CFG_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000400)
enum bimc_s_scmo_slv_interleave_cfg {
	S_SCMO_SLV_INTERLEAVE_CFG_RMSK			= 0xff,
	S_SCMO_SLV_INTERLEAVE_CFG_INTERLEAVE_CS1_BMSK	= 0x10,
	S_SCMO_SLV_INTERLEAVE_CFG_INTERLEAVE_CS1_SHFT	= 0x4,
	S_SCMO_SLV_INTERLEAVE_CFG_INTERLEAVE_CS0_BMSK	= 0x1,
	S_SCMO_SLV_INTERLEAVE_CFG_INTERLEAVE_CS0_SHFT	= 0x0,
};

#define S_SCMO_ADDR_BASE_CSn_ADDR(b, n, o)	\
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000410 + 0x4 * (o))
enum bimc_s_scmo_addr_base_csn {
	S_SCMO_ADDR_BASE_CSn_RMSK			= 0xffff,
	S_SCMO_ADDR_BASE_CSn_MAXn			= 1,
	S_SCMO_ADDR_BASE_CSn_ADDR_BASE_BMSK		= 0xfc,
	S_SCMO_ADDR_BASE_CSn_ADDR_BASE_SHFT		= 0x2,
};

#define S_SCMO_ADDR_MAP_CSn_ADDR(b, n, o) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000420 + 0x4 * (o))
enum bimc_s_scmo_addr_map_csn {
	S_SCMO_ADDR_MAP_CSn_RMSK		= 0xffff,
	S_SCMO_ADDR_MAP_CSn_MAXn		= 1,
	S_SCMO_ADDR_MAP_CSn_RANK_EN_BMSK	= 0x8000,
	S_SCMO_ADDR_MAP_CSn_RANK_EN_SHFT	= 0xf,
	S_SCMO_ADDR_MAP_CSn_ADDR_MODE_BMSK	= 0x1000,
	S_SCMO_ADDR_MAP_CSn_ADDR_MODE_SHFT	= 0xc,
	S_SCMO_ADDR_MAP_CSn_BANK_SIZE_BMSK	= 0x100,
	S_SCMO_ADDR_MAP_CSn_BANK_SIZE_SHFT	= 0x8,
	S_SCMO_ADDR_MAP_CSn_ROW_SIZE_BMSK	= 0x30,
	S_SCMO_ADDR_MAP_CSn_ROW_SIZE_SHFT	= 0x4,
	S_SCMO_ADDR_MAP_CSn_COL_SIZE_BMSK	= 0x3,
	S_SCMO_ADDR_MAP_CSn_COL_SIZE_SHFT	= 0x0,
};

#define S_SCMO_ADDR_MASK_CSn_ADDR(b, n, o) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000430 + 0x4 * (0))
enum bimc_s_scmo_addr_mask_csn {
	S_SCMO_ADDR_MASK_CSn_RMSK		= 0xffff,
	S_SCMO_ADDR_MASK_CSn_MAXn		= 1,
	S_SCMO_ADDR_MASK_CSn_ADDR_MASK_BMSK	= 0xfc,
	S_SCMO_ADDR_MASK_CSn_ADDR_MASK_SHFT	= 0x2,
};

#define S_SCMO_SLV_STATUS_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000450)
enum bimc_s_scmo_slv_status {
	S_SCMO_SLV_STATUS_RMSK				= 0xff3,
	S_SCMO_SLV_STATUS_GLOBAL_MONS_IN_USE_BMSK	= 0xff0,
	S_SCMO_SLV_STATUS_GLOBAL_MONS_IN_USE_SHFT	= 0x4,
	S_SCMO_SLV_STATUS_SLAVE_IDLE_BMSK		= 0x3,
	S_SCMO_SLV_STATUS_SLAVE_IDLE_SHFT		= 0x0,
};

#define S_SCMO_CMD_BUF_CFG_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000500)
enum bimc_s_scmo_cmd_buf_cfg {
	S_SCMO_CMD_BUF_CFG_RMSK				= 0xf1f,
	S_SCMO_CMD_BUF_CFG_CMD_ORDERING_BMSK		= 0x300,
	S_SCMO_CMD_BUF_CFG_CMD_ORDERING_SHFT		= 0x8,
	S_SCMO_CMD_BUF_CFG_HP_CMD_AREQPRIO_MAP_BMSK	= 0x10,
	S_SCMO_CMD_BUF_CFG_HP_CMD_AREQPRIO_MAP_SHFT	= 0x4,
	S_SCMO_CMD_BUF_CFG_HP_CMD_Q_DEPTH_BMSK		= 0x7,
	S_SCMO_CMD_BUF_CFG_HP_CMD_Q_DEPTH_SHFT		= 0x0,
};

#define S_SCM_CMD_BUF_STATUS_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000520)
enum bimc_s_scm_cmd_buf_status {
	S_SCMO_CMD_BUF_STATUS_RMSK				= 0x77,
	S_SCMO_CMD_BUF_STATUS_HP_CMD_BUF_ENTRIES_IN_USE_BMSK	= 0x70,
	S_SCMO_CMD_BUF_STATUS_HP_CMD_BUF_ENTRIES_IN_USE_SHFT	= 0x4,
	S_SCMO_CMD_BUF_STATUS_LP_CMD_BUF_ENTRIES_IN_USE_BMSK	= 0x7,
	S_SCMO_CMD_BUF_STATUS_LP_CMD_BUF_ENTRIES_IN_USE_SHFT	= 0x0,
};

#define S_SCMO_RCH_SEL_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000540)
enum bimc_s_scmo_rch_sel {
	S_SCMO_RCH_SEL_RMSK			= 0xffffffff,
	S_SCMO_CMD_BUF_STATUS_RCH_PORTS_BMSK	= 0xffffffff,
	S_SCMO_CMD_BUF_STATUS_RCH_PORTS_SHFT	= 0x0,
};

#define S_SCMO_RCH_BKPR_CFG_ADDR(b, n) \
		(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000544)
enum bimc_s_scmo_rch_bkpr_cfg {
	S_SCMO_RCH_BKPR_CFG_RMSK			= 0xffffffff,
	S_SCMO_RCH_BKPR_CFG_RCH1_FIFO_BKPR_HI_TH_BMSK	= 0x3f000000,
	S_SCMO_RCH_BKPR_CFG_RCH1_FIFO_BKPR_HI_TH_SHFT	= 0x18,
	S_SCMO_RCH_BKPR_CFG_RCH1_FIFO_BKPR_LO_TH_BMSK	= 0x3f0000,
	S_SCMO_RCH_BKPR_CFG_RCH1_FIFO_BKPR_LO_TH_SHFT	= 0x10,
	S_SCMO_RCH_BKPR_CFG_RCH0_FIFO_BKPR_HI_TH_BMSK	= 0x3f00,
	S_SCMO_RCH_BKPR_CFG_RCH0_FIFO_BKPR_HI_TH_SHFT	= 0x8,
	S_SCMO_RCH_BKPR_CFG_RCH0_FIFO_BKPR_LO_TH_BMSK	= 0x3f,
	S_SCMO_RCH_BKPR_CFG_RCH0_FIFO_BKPR_LO_TH_SHFT	= 0x0,
};

#define S_SCMO_RCH_STATUS_ADDR(b, n) \
		(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000560)
enum bimc_s_scmo_rch_status {
	S_SCMO_RCH_STATUS_RMSK				= 0x33333,
	S_SCMO_RCH_STATUS_PRQ_FIFO_FULL_BMSK		= 0x20000,
	S_SCMO_RCH_STATUS_PRQ_FIFO_FULL_SHFT		= 0x11,
	S_SCMO_RCH_STATUS_PRQ_FIFO_EMPTY_BMSK		= 0x10000,
	S_SCMO_RCH_STATUS_PRQ_FIFO_EMPTY_SHFT		= 0x10,
	S_SCMO_RCH_STATUS_RCH1_QUAL_FIFO_FULL_BMSK	= 0x2000,
	S_SCMO_RCH_STATUS_RCH1_QUAL_FIFO_FULL_SHFT	= 0xd,
	S_SCMO_RCH_STATUS_RCH1_QUAL_FIFO_EMPTY_BMSK	= 0x1000,
	S_SCMO_RCH_STATUS_RCH1_QUAL_FIFO_EMPTY_SHFT	= 0xc,
	S_SCMO_RCH_STATUS_RCH1_DATA_FIFO_FULL_BMSK	= 0x200,
	S_SCMO_RCH_STATUS_RCH1_DATA_FIFO_FULL_SHFT	= 0x9,
	S_SCMO_RCH_STATUS_RCH1_DATA_FIFO_EMPTY_BMSK	= 0x100,
	S_SCMO_RCH_STATUS_RCH1_DATA_FIFO_EMPTY_SHFT	= 0x8,
	S_SCMO_RCH_STATUS_RCH0_QUAL_FIFO_FULL_BMSK	= 0x20,
	S_SCMO_RCH_STATUS_RCH0_QUAL_FIFO_FULL_SHFT	= 0x5,
	S_SCMO_RCH_STATUS_RCH0_QUAL_FIFO_EMPTY_BMSK	= 0x10,
	S_SCMO_RCH_STATUS_RCH0_QUAL_FIFO_EMPTY_SHFT	= 0x4,
	S_SCMO_RCH_STATUS_RCH0_DATA_FIFO_FULL_BMSK	= 0x2,
	S_SCMO_RCH_STATUS_RCH0_DATA_FIFO_FULL_SHFT	= 0x1,
	S_SCMO_RCH_STATUS_RCH0_DATA_FIFO_EMPTY_BMSK	= 0x1,
	S_SCMO_RCH_STATUS_RCH0_DATA_FIFO_EMPTY_SHFT	= 0x0,
};

#define S_SCMO_WCH_BUF_CFG_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000580)
enum bimc_s_scmo_wch_buf_cfg {
	S_SCMO_WCH_BUF_CFG_RMSK				= 0xff,
	S_SCMO_WCH_BUF_CFG_WRITE_BLOCK_READ_BMSK	= 0x10,
	S_SCMO_WCH_BUF_CFG_WRITE_BLOCK_READ_SHFT	= 0x4,
	S_SCMO_WCH_BUF_CFG_COALESCE_EN_BMSK		= 0x1,
	S_SCMO_WCH_BUF_CFG_COALESCE_EN_SHFT		= 0x0,
};

#define S_SCMO_WCH_STATUS_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x000005a0)
enum bimc_s_scmo_wch_status {
	S_SCMO_WCH_STATUS_RMSK				= 0x333,
	S_SCMO_WCH_STATUS_BRESP_FIFO_FULL_BMSK		= 0x200,
	S_SCMO_WCH_STATUS_BRESP_FIFO_FULL_SHFT		= 0x9,
	S_SCMO_WCH_STATUS_BRESP_FIFO_EMPTY_BMSK		= 0x100,
	S_SCMO_WCH_STATUS_BRESP_FIFO_EMPTY_SHFT		= 0x8,
	S_SCMO_WCH_STATUS_WDATA_FIFO_FULL_BMSK		= 0x20,
	S_SCMO_WCH_STATUS_WDATA_FIFO_FULL_SHFT		= 0x5,
	S_SCMO_WCH_STATUS_WDATA_FIFO_EMPTY_BMSK		= 0x10,
	S_SCMO_WCH_STATUS_WDATA_FIFO_EMPTY_SHFT		= 0x4,
	S_SCMO_WCH_STATUS_WBUF_FULL_BMSK		= 0x2,
	S_SCMO_WCH_STATUS_WBUF_FULL_SHFT		= 0x1,
	S_SCMO_WCH_STATUS_WBUF_EMPTY_BMSK		= 0x1,
	S_SCMO_WCH_STATUS_WBUF_EMPTY_SHFT		= 0x0,
};

#define S_SCMO_FLUSH_CFG_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x000005c0)
enum bimc_s_scmo_flush_cfg {
	S_SCMO_FLUSH_CFG_RMSK				= 0xffffffff,
	S_SCMO_FLUSH_CFG_FLUSH_IN_ORDER_BMSK		= 0x10000000,
	S_SCMO_FLUSH_CFG_FLUSH_IN_ORDER_SHFT		= 0x1c,
	S_SCMO_FLUSH_CFG_FLUSH_IDLE_DELAY_BMSK		= 0x3ff0000,
	S_SCMO_FLUSH_CFG_FLUSH_IDLE_DELAY_SHFT		= 0x10,
	S_SCMO_FLUSH_CFG_FLUSH_UPPER_LIMIT_BMSK		= 0xf00,
	S_SCMO_FLUSH_CFG_FLUSH_UPPER_LIMIT_SHFT		= 0x8,
	S_SCMO_FLUSH_CFG_FLUSH_LOWER_LIMIT_BMSK		= 0xf,
	S_SCMO_FLUSH_CFG_FLUSH_LOWER_LIMIT_SHFT		= 0x0,
};

#define S_SCMO_FLUSH_CMD_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x000005c4)
enum bimc_s_scmo_flush_cmd {
	S_SCMO_FLUSH_CMD_RMSK				= 0xf,
	S_SCMO_FLUSH_CMD_FLUSH_ALL_BUF_BMSK		= 0x3,
	S_SCMO_FLUSH_CMD_FLUSH_ALL_BUF_SHFT		= 0x0,
};

#define S_SCMO_CMD_OPT_CFG0_ADDR(b, n) \
	(S_SCM0_REG_BASE(b) + (0x8000 * (n)) + 0x00000700)
enum bimc_s_scmo_cmd_opt_cfg0 {
	S_SCMO_CMD_OPT_CFG0_RMSK		= 0xffffff,
	S_SCMO_CMD_OPT_CFG0_IGNORE_BANK_UNAVL_BMSK	= 0x100000,
	S_SCMO_CMD_OPT_CFG0_IGNORE_BANK_UNAVL_SHFT	= 0x14,
	S_SCMO_CMD_OPT_CFG0_MASK_CMDOUT_PRI_BMSK	= 0x10000,
	S_SCMO_CMD_OPT_CFG0_MASK_CMDOUT_PRI_SHFT	= 0x10,
	S_SCMO_CMD_OPT_CFG0_DPE_CMD_REORDERING_BMSK	= 0x1000,
	S_SCMO_CMD_OPT_CFG0_DPE_CMD_REORDERING_SHFT	= 0xc,
	S_SCMO_CMD_OPT_CFG0_WR_OPT_EN_BMSK		= 0x100,
	S_SCMO_CMD_OPT_CFG0_WR_OPT_EN_SHFT		= 0x8,
	S_SCMO_CMD_OPT_CFG0_RD_OPT_EN_BMSK		= 0x10,
	S_SCMO_CMD_OPT_CFG0_RD_OPT_EN_SHFT		= 0x4,
	S_SCMO_CMD_OPT_CFG0_PAGE_MGMT_POLICY_BMSK	= 0x1,
	S_SCMO_CMD_OPT_CFG0_PAGE_MGMT_POLICY_SHFT	= 0x0,
};

#define S_SCMO_CMD_OPT_CFG1_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000704)
enum bimc_s_scmo_cmd_opt_cfg1 {
	S_SCMO_CMD_OPT_CFG1_RMSK			= 0xffffffff,
	S_SCMO_CMD_OPT_CFG1_HSTP_CMD_TIMEOUT_BMSK	= 0x1f000000,
	S_SCMO_CMD_OPT_CFG1_HSTP_CMD_TIMEOUT_SHFT	= 0x18,
	S_SCMO_CMD_OPT_CFG1_HP_CMD_TIMEOUT_BMSK		= 0x1f0000,
	S_SCMO_CMD_OPT_CFG1_HP_CMD_TIMEOUT_SHFT		= 0x10,
	S_SCMO_CMD_OPT_CFG1_MP_CMD_TIMEOUT_BMSK		= 0x1f00,
	S_SCMO_CMD_OPT_CFG1_MP_CMD_TIMEOUT_SHFT		= 0x8,
	S_SCMO_CMD_OPT_CFG1_LP_CMD_TIMEOUT_BMSK		= 0x1f,
	S_SCMO_CMD_OPT_CFG1_LP_CMD_TIMEOUT_SHFT		= 0x0,
};

#define S_SCMO_CMD_OPT_CFG2_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x00000708)
enum bimc_s_scmo_cmd_opt_cfg2 {
	S_SCMO_CMD_OPT_CFG2_RMSK			= 0xff,
	S_SCMO_CMD_OPT_CFG2_RWOPT_CMD_TIMEOUT_BMSK	= 0xf,
	S_SCMO_CMD_OPT_CFG2_RWOPT_CMD_TIMEOUT_SHFT	= 0x0,
};

#define S_SCMO_CMD_OPT_CFG3_ADDR(b, n) \
	(S_SCMO_REG_BASE(b) + (0x8000 * (n)) + 0x0000070c)
enum bimc_s_scmo_cmd_opt_cfg3 {
	S_SCMO_CMD_OPT_CFG3_RMSK			= 0xff,
	S_SCMO_CMD_OPT_CFG3_FLUSH_CMD_TIMEOUT_BMSK	= 0xf,
	S_SCMO_CMD_OPT_CFG3_FLUSH_CMD_TIMEOUT_SHFT	= 0x0,
};

/* S_SWAY_GENERIC */
#define S_SWAY_REG_BASE(b)	((b) + 0x00048000)

#define S_SWAY_CONFIG_INFO_0_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000020)
enum bimc_s_sway_config_info_0 {
	S_SWAY_CONFIG_INFO_0_RMSK		= 0xff0000ff,
	S_SWAY_CONFIG_INFO_0_SYNC_MODE_BMSK	= 0xff000000,
	S_SWAY_CONFIG_INFO_0_SYNC_MODE_SHFT	= 0x18,
	S_SWAY_CONFIG_INFO_0_FUNC_BMSK		= 0xff,
	S_SWAY_CONFIG_INFO_0_FUNC_SHFT		= 0x0,
};

#define S_SWAY_CONFIG_INFO_1_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000030)
enum bimc_s_sway_config_info_1 {
	S_SWAY_CONFIG_INFO_1_RMSK			= 0xffffffff,
	S_SWAY_CONFIG_INFO_1_MPORT_CONNECTIVITY_BMSK	= 0xffffffff,
	S_SWAY_CONFIG_INFO_1_MPORT_CONNECTIVITY_SHFT	= 0x0,
};

#define S_SWAY_CONFIG_INFO_2_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000040)
enum bimc_s_sway_config_info_2 {
	S_SWAY_CONFIG_INFO_2_RMSK			= 0xffff0000,
	S_SWAY_CONFIG_INFO_2_MPORT_CONNECTIVITY_BMSK	= 0xffff0000,
	S_SWAY_CONFIG_INFO_2_MPORT_CONNECTIVITY_SHFT	= 0x10,
};

#define S_SWAY_CONFIG_INFO_3_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000050)
enum bimc_s_sway_config_info_3 {
	S_SWAY_CONFIG_INFO_3_RMSK			= 0xffffffff,
	S_SWAY_CONFIG_INFO_3_RCH0_DEPTH_BMSK		= 0xff000000,
	S_SWAY_CONFIG_INFO_3_RCH0_DEPTH_SHFT		= 0x18,
	S_SWAY_CONFIG_INFO_3_BCH_DEPTH_BMSK		= 0xff0000,
	S_SWAY_CONFIG_INFO_3_BCH_DEPTH_SHFT		= 0x10,
	S_SWAY_CONFIG_INFO_3_WCH_DEPTH_BMSK		= 0xff,
	S_SWAY_CONFIG_INFO_3_WCH_DEPTH_SHFT		= 0x0,
};

#define S_SWAY_CONFIG_INFO_4_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000060)
enum bimc_s_sway_config_info_4 {
	S_SWAY_CONFIG_INFO_4_RMSK			= 0x800000ff,
	S_SWAY_CONFIG_INFO_4_DUAL_RCH_EN_BMSK		= 0x80000000,
	S_SWAY_CONFIG_INFO_4_DUAL_RCH_EN_SHFT		= 0x1f,
	S_SWAY_CONFIG_INFO_4_RCH1_DEPTH_BMSK		= 0xff,
	S_SWAY_CONFIG_INFO_4_RCH1_DEPTH_SHFT		= 0x0,
};

#define S_SWAY_CONFIG_INFO_5_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000070)
enum bimc_s_sway_config_info_5 {
	S_SWAY_CONFIG_INFO_5_RMSK			= 0x800000ff,
	S_SWAY_CONFIG_INFO_5_QCH_EN_BMSK		= 0x80000000,
	S_SWAY_CONFIG_INFO_5_QCH_EN_SHFT		= 0x1f,
	S_SWAY_CONFIG_INFO_5_QCH_DEPTH_BMSK		= 0xff,
	S_SWAY_CONFIG_INFO_5_QCH_DEPTH_SHFT		= 0x0,
};

#define S_SWAY_CONFIG_INFO_6_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000080)
enum bimc_s_sway_config_info_6 {
	S_SWAY_CONFIG_INFO_6_RMSK			= 0x1,
	S_SWAY_CONFIG_INFO_6_S2SW_PIPELINE_EN_BMSK	= 0x1,
	S_SWAY_CONFIG_INFO_6_S2SW_PIPELINE_EN_SHFT	= 0x0,
};

#define S_SWAY_INT_STATUS_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000100)
enum bimc_s_sway_int_status {
	S_SWAY_INT_STATUS_RMSK		= 0x3,
	S_SWAY_INT_STATUS_RFU_BMSK	= 0x3,
	S_SWAY_INT_STATUS_RFU_SHFT	= 0x0,
};

#define S_SWAY_INT_CLR_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000108)
enum bimc_s_sway_int_clr {
	S_SWAY_INT_CLR_RMSK		= 0x3,
	S_SWAY_INT_CLR_RFU_BMSK		= 0x3,
	S_SWAY_INT_CLR_RFU_SHFT		= 0x0,
};


#define S_SWAY_INT_EN_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x0000010c)
enum bimc_s_sway_int_en {
	S_SWAY_INT_EN_RMSK		= 0x3,
	S_SWAY_INT_EN_RFU_BMSK		= 0x3,
	S_SWAY_INT_EN_RFU_SHFT		= 0x0,
};

#define S_SWAY_CLK_CTRL_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000200)
enum bimc_s_sway_clk_ctrl {
	S_SWAY_CLK_CTRL_RMSK				= 0x3,
	S_SWAY_CLK_CTRL_SLAVE_CLK_GATING_EN_BMSK	= 0x2,
	S_SWAY_CLK_CTRL_SLAVE_CLK_GATING_EN_SHFT	= 0x1,
	S_SWAY_CLK_CTRL_CORE_CLK_GATING_EN_BMSK		= 0x1,
	S_SWAY_CLK_CTRL_CORE_CLK_GATING_EN_SHFT		= 0x0,
};

#define S_SWAY_RCH_SEL_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000210)
enum bimc_s_sway_rch_sel {
	S_SWAY_RCH_SEL_RMSK		= 0x7f,
	S_SWAY_RCH_SEL_UNUSED_BMSK	= 0x7f,
	S_SWAY_RCH_SEL_UNUSED_SHFT	= 0x0,
};


#define S_SWAY_MAX_OUTSTANDING_REQS_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000220)
enum bimc_s_sway_max_outstanding_reqs {
	S_SWAY_MAX_OUTSTANDING_REQS_RMSK	= 0xffff,
	S_SWAY_MAX_OUTSTANDING_REQS_WRITE_BMSK	= 0xff00,
	S_SWAY_MAX_OUTSTANDING_REQS_WRITE_SHFT	= 0x8,
	S_SWAY_MAX_OUTSTANDING_REQS_READ_BMSK	= 0xff,
	S_SWAY_MAX_OUTSTANDING_REQS_READ_SHFT	= 0x0,
};


#define S_SWAY_BUF_STATUS_0_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000400)
enum bimc_s_sway_buf_status_0 {
	S_SWAY_BUF_STATUS_0_RMSK			= 0xf0300f03,
	S_SWAY_BUF_STATUS_0_RCH0_DATA_RD_FULL_BMSK	= 0x80000000,
	S_SWAY_BUF_STATUS_0_RCH0_DATA_RD_FULL_SHFT	= 0x1f,
	S_SWAY_BUF_STATUS_0_RCH0_DATA_RD_EMPTY_BMSK	= 0x40000000,
	S_SWAY_BUF_STATUS_0_RCH0_DATA_RD_EMPTY_SHFT	= 0x1e,
	S_SWAY_BUF_STATUS_0_RCH0_CTRL_RD_FULL_BMSK	= 0x20000000,
	S_SWAY_BUF_STATUS_0_RCH0_CTRL_RD_FULL_SHFT	= 0x1d,
	S_SWAY_BUF_STATUS_0_RCH0_CTRL_RD_EMPTY_BMSK	= 0x10000000,
	S_SWAY_BUF_STATUS_0_RCH0_CTRL_RD_EMPTY_SHFT	= 0x1c,
	S_SWAY_BUF_STATUS_0_BCH_RD_FULL_BMSK		= 0x200000,
	S_SWAY_BUF_STATUS_0_BCH_RD_FULL_SHFT		= 0x15,
	S_SWAY_BUF_STATUS_0_BCH_RD_EMPTY_BMSK		= 0x100000,
	S_SWAY_BUF_STATUS_0_BCH_RD_EMPTY_SHFT		= 0x14,
	S_SWAY_BUF_STATUS_0_WCH_DATA_WR_FULL_BMSK	= 0x800,
	S_SWAY_BUF_STATUS_0_WCH_DATA_WR_FULL_SHFT	= 0xb,
	S_SWAY_BUF_STATUS_0_WCH_DATA_WR_EMPTY_BMSK	= 0x400,
	S_SWAY_BUF_STATUS_0_WCH_DATA_WR_EMPTY_SHFT	= 0xa,
	S_SWAY_BUF_STATUS_0_WCH_CTRL_WR_FULL_BMSK	= 0x200,
	S_SWAY_BUF_STATUS_0_WCH_CTRL_WR_FULL_SHFT	= 0x9,
	S_SWAY_BUF_STATUS_0_WCH_CTRL_WR_EMPTY_BMSK	= 0x100,
	S_SWAY_BUF_STATUS_0_WCH_CTRL_WR_EMPTY_SHFT	= 0x8,
	S_SWAY_BUF_STATUS_0_ACH_WR_FULL_BMSK		= 0x2,
	S_SWAY_BUF_STATUS_0_ACH_WR_FULL_SHFT		= 0x1,
	S_SWAY_BUF_STATUS_0_ACH_WR_EMPTY_BMSK		= 0x1,
	S_SWAY_BUF_STATUS_0_ACH_WR_EMPTY_SHFT		= 0x0,
};

#define S_SWAY_BUF_STATUS_1_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000410)
enum bimc_s_sway_buf_status_1 {
	S_SWAY_BUF_STATUS_1_RMSK			= 0xf0,
	S_SWAY_BUF_STATUS_1_RCH1_DATA_RD_FULL_BMSK	= 0x80,
	S_SWAY_BUF_STATUS_1_RCH1_DATA_RD_FULL_SHFT	= 0x7,
	S_SWAY_BUF_STATUS_1_RCH1_DATA_RD_EMPTY_BMSK	= 0x40,
	S_SWAY_BUF_STATUS_1_RCH1_DATA_RD_EMPTY_SHFT	= 0x6,
	S_SWAY_BUF_STATUS_1_RCH1_CTRL_RD_FULL_BMSK	= 0x20,
	S_SWAY_BUF_STATUS_1_RCH1_CTRL_RD_FULL_SHFT	= 0x5,
	S_SWAY_BUF_STATUS_1_RCH1_CTRL_RD_EMPTY_BMSK	= 0x10,
	S_SWAY_BUF_STATUS_1_RCH1_CTRL_RD_EMPTY_SHFT	= 0x4,
};

#define S_SWAY_BUF_STATUS_2_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000420)
enum bimc_s_sway_buf_status_2 {
	S_SWAY_BUF_STATUS_2_RMSK		= 0x30,
	S_SWAY_BUF_STATUS_2_QCH_RD_FULL_BMSK	= 0x20,
	S_SWAY_BUF_STATUS_2_QCH_RD_FULL_SHFT	= 0x5,
	S_SWAY_BUF_STATUS_2_QCH_RD_EMPTY_BMSK	= 0x10,
	S_SWAY_BUF_STATUS_2_QCH_RD_EMPTY_SHFT	= 0x4,
};

/* S_ARB_GENERIC */

#define S_ARB_REG_BASE(b)	((b) + 0x00049000)

#define S_ARB_COMPONENT_INFO_ADDR(b, n) \
	(S_SWAY_REG_BASE(b) + (0x8000 * (n)) + 0x00000000)
enum bimc_s_arb_component_info {
	S_ARB_COMPONENT_INFO_RMSK		= 0xffffff,
	S_ARB_COMPONENT_INFO_INSTANCE_BMSK	= 0xff0000,
	S_ARB_COMPONENT_INFO_INSTANCE_SHFT	= 0x10,
	S_ARB_COMPONENT_INFO_SUB_TYPE_BMSK	= 0xff00,
	S_ARB_COMPONENT_INFO_SUB_TYPE_SHFT	= 0x8,
	S_ARB_COMPONENT_INFO_TYPE_BMSK		= 0xff,
	S_ARB_COMPONENT_INFO_TYPE_SHFT		= 0x0,
};

#define S_ARB_CONFIG_INFO_0_ADDR(b, n) \
		(S_ARB_REG_BASE(b) + (0x8000 * (n)) + 0x00000020)
enum bimc_s_arb_config_info_0 {
	S_ARB_CONFIG_INFO_0_RMSK			= 0x800000ff,
	S_ARB_CONFIG_INFO_0_ARB2SW_PIPELINE_EN_BMSK	= 0x80000000,
	S_ARB_CONFIG_INFO_0_ARB2SW_PIPELINE_EN_SHFT	= 0x1f,
	S_ARB_CONFIG_INFO_0_FUNC_BMSK			= 0xff,
	S_ARB_CONFIG_INFO_0_FUNC_SHFT			= 0x0,
};

#define S_ARB_CONFIG_INFO_1_ADDR(b, n) \
		(S_ARB_REG_BASE(b) + (0x8000 * (n)) + 0x00000030)
enum bimc_s_arb_config_info_1 {
	S_ARB_CONFIG_INFO_1_RMSK			= 0xffffffff,
	S_ARB_CONFIG_INFO_1_MPORT_CONNECTIVITY_BMSK	= 0xffffffff,
	S_ARB_CONFIG_INFO_1_MPORT_CONNECTIVITY_SHFT	= 0x0,
};

#define S_ARB_CLK_CTRL_ADDR(b) \
	(S_ARB_REG_BASE(b) + (0x8000 * (n)) + 0x00000200)
enum bimc_s_arb_clk_ctrl {
	S_ARB_CLK_CTRL_RMSK				= 0x1,
	S_ARB_CLK_CTRL_SLAVE_CLK_GATING_EN_BMSK		= 0x2,
	S_ARB_CLK_CTRL_SLAVE_CLK_GATING_EN_SHFT		= 0x1,
	S_ARB_CLK_CTRL_CORE_CLK_GATING_EN_BMSK		= 0x1,
	S_ARB_CLK_CTRL_CORE_CLK_GATING_EN_SHFT		= 0x0,
	S_ARB_CLK_CTRL_CLK_GATING_EN_BMSK		= 0x1,
	S_ARB_CLK_CTRL_CLK_GATING_EN_SHFT		= 0x0,
};

#define S_ARB_MODE_ADDR(b, n) \
	(S_ARB_REG_BASE(b) + (0x8000 * (n)) + 0x00000210)
enum bimc_s_arb_mode {
	S_ARB_MODE_RMSK				= 0xf0000001,
	S_ARB_MODE_WR_GRANTS_AHEAD_BMSK		= 0xf0000000,
	S_ARB_MODE_WR_GRANTS_AHEAD_SHFT		= 0x1c,
	S_ARB_MODE_PRIO_RR_EN_BMSK		= 0x1,
	S_ARB_MODE_PRIO_RR_EN_SHFT		= 0x0,
};

#define BKE_HEALTH_MASK \
	(M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_BMSK |\
	M_BKE_HEALTH_0_CONFIG_AREQPRIO_BMSK |\
	M_BKE_HEALTH_0_CONFIG_PRIOLVL_BMSK)

#define BKE_HEALTH_VAL(limit, areq, plvl) \
	((((limit) << M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_SHFT) & \
	M_BKE_HEALTH_0_CONFIG_LIMIT_CMDS_BMSK) | \
	(((areq) << M_BKE_HEALTH_0_CONFIG_AREQPRIO_SHFT) & \
	M_BKE_HEALTH_0_CONFIG_AREQPRIO_BMSK) | \
	(((plvl) << M_BKE_HEALTH_0_CONFIG_PRIOLVL_SHFT) & \
	M_BKE_HEALTH_0_CONFIG_PRIOLVL_BMSK))

#define MAX_GRANT_PERIOD \
	(M_BKE_GP_GP_BMSK >> \
	M_BKE_GP_GP_SHFT)

#define MAX_GC \
	(M_BKE_GC_GC_BMSK >> \
	(M_BKE_GC_GC_SHFT + 1))

static int bimc_div(int64_t *a, uint32_t b)
{
	if ((*a > 0) && (*a < b)) {
		*a = 0;
		return 1;
	} else {
		return do_div(*a, b);
	}
}

#define ENABLE(val) ((val) == 1 ? 1 : 0)
void msm_bus_bimc_set_mas_clk_gate(struct msm_bus_bimc_info *binfo,
	uint32_t mas_index, struct msm_bus_bimc_clk_gate *bgate)
{
	uint32_t val, mask, reg_val;
	void __iomem *addr;

	reg_val = readl_relaxed(M_CLK_CTRL_ADDR(binfo->base,
			mas_index)) & M_CLK_CTRL_RMSK;
	addr = M_CLK_CTRL_ADDR(binfo->base, mas_index);
	mask = (M_CLK_CTRL_MAS_CLK_GATING_EN_BMSK |
		M_CLK_CTRL_CORE_CLK_GATING_EN_BMSK);
	val = (bgate->core_clk_gate_en <<
		M_CLK_CTRL_MAS_CLK_GATING_EN_SHFT) |
		bgate->port_clk_gate_en;
	writel_relaxed(((reg_val & (~mask)) | (val & mask)), addr);
	/* Ensure clock gating enable mask is set before exiting */
	wmb();
}

void msm_bus_bimc_arb_en(struct msm_bus_bimc_info *binfo,
	uint32_t slv_index, bool en)
{
	uint32_t reg_val, reg_mask_val, enable, val;

	reg_mask_val = (readl_relaxed(S_ARB_CONFIG_INFO_0_ADDR(binfo->
		base, slv_index)) & S_ARB_CONFIG_INFO_0_FUNC_BMSK)
		>> S_ARB_CONFIG_INFO_0_FUNC_SHFT;
	enable = ENABLE(en);
	val = enable << S_ARB_MODE_PRIO_RR_EN_SHFT;
	if (reg_mask_val == BIMC_ARB_MODE_PRIORITY_RR) {
		reg_val = readl_relaxed(S_ARB_CONFIG_INFO_0_ADDR(binfo->
			base, slv_index)) & S_ARB_MODE_RMSK;
		writel_relaxed(((reg_val & (~(S_ARB_MODE_PRIO_RR_EN_BMSK))) |
			(val & S_ARB_MODE_PRIO_RR_EN_BMSK)),
			S_ARB_MODE_ADDR(binfo->base, slv_index));
		/* Ensure arbitration mode is set before returning */
		wmb();
	}
}

static void set_qos_mode(void __iomem *baddr, uint32_t index, uint32_t val0,
	uint32_t val1, uint32_t val2)
{
	uint32_t reg_val, val;

	reg_val = readl_relaxed(M_PRIOLVL_OVERRIDE_ADDR(baddr,
		index)) & M_PRIOLVL_OVERRIDE_RMSK;
	val = val0 << M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_SHFT;
	writel_relaxed(((reg_val & ~(M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK))
		| (val & M_PRIOLVL_OVERRIDE_OVERRIDE_PRIOLVL_BMSK)),
		M_PRIOLVL_OVERRIDE_ADDR(baddr, index));
	reg_val = readl_relaxed(M_RD_CMD_OVERRIDE_ADDR(baddr, index)) &
		M_RD_CMD_OVERRIDE_RMSK;
	val = val1 << M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT;
	writel_relaxed(((reg_val & ~(M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK
		)) | (val & M_RD_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK)),
		M_RD_CMD_OVERRIDE_ADDR(baddr, index));
	reg_val = readl_relaxed(M_WR_CMD_OVERRIDE_ADDR(baddr, index)) &
		M_WR_CMD_OVERRIDE_RMSK;
	val = val2 << M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_SHFT;
	writel_relaxed(((reg_val & ~(M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK
		)) | (val & M_WR_CMD_OVERRIDE_OVERRIDE_AREQPRIO_BMSK)),
		M_WR_CMD_OVERRIDE_ADDR(baddr, index));
	/* Ensure the priority register writes go through */
	wmb();
}

static void msm_bus_bimc_set_qos_mode(void __iomem *base,
	uint32_t mas_index, uint8_t qmode_sel)
{
	uint32_t reg_val, val;

	switch (qmode_sel) {
	case BIMC_QOS_MODE_FIXED:
		reg_val = readl_relaxed(M_BKE_EN_ADDR(base,
			mas_index));
		writel_relaxed((reg_val & (~M_BKE_EN_EN_BMSK)),
			M_BKE_EN_ADDR(base, mas_index));
		/* Ensure that the book-keeping register writes
		 * go through before setting QoS mode.
		 * QoS mode registers might write beyond 1K
		 * boundary in future
		 */
		wmb();
		set_qos_mode(base, mas_index, 1, 1, 1);
		break;

	case BIMC_QOS_MODE_BYPASS:
		reg_val = readl_relaxed(M_BKE_EN_ADDR(base,
			mas_index));
		writel_relaxed((reg_val & (~M_BKE_EN_EN_BMSK)),
			M_BKE_EN_ADDR(base, mas_index));
		/* Ensure that the book-keeping register writes
		 * go through before setting QoS mode.
		 * QoS mode registers might write beyond 1K
		 * boundary in future
		 */
		wmb();
		set_qos_mode(base, mas_index, 0, 0, 0);
		break;

	case BIMC_QOS_MODE_REGULATOR:
	case BIMC_QOS_MODE_LIMITER:
		set_qos_mode(base, mas_index, 0, 0, 0);
		reg_val = readl_relaxed(M_BKE_EN_ADDR(base,
			mas_index));
		val = 1 << M_BKE_EN_EN_SHFT;
		/* Ensure that the book-keeping register writes
		 * go through before setting QoS mode.
		 * QoS mode registers might write beyond 1K
		 * boundary in future
		 */
		wmb();
		writel_relaxed(((reg_val & (~M_BKE_EN_EN_BMSK)) | (val &
			M_BKE_EN_EN_BMSK)), M_BKE_EN_ADDR(base,
			mas_index));
		break;
	default:
		break;
	}
}

static void set_qos_prio_rl(void __iomem *addr, uint32_t rmsk,
	uint8_t index, struct msm_bus_bimc_qos_mode *qmode)
{
	uint32_t reg_val, val0, val;

	/* Note, addr is already passed with right mas_index */
	reg_val = readl_relaxed(addr) & rmsk;
	val0 = BKE_HEALTH_VAL(qmode->rl.qhealth[index].limit_commands,
		qmode->rl.qhealth[index].areq_prio,
		qmode->rl.qhealth[index].prio_level);
	val = ((reg_val & (~(BKE_HEALTH_MASK))) | (val0 & BKE_HEALTH_MASK));
	writel_relaxed(val, addr);
	/* Ensure that priority for regulator/limiter modes are
	 * set before returning
	 */
	wmb();

}

static void msm_bus_bimc_set_qos_prio(void __iomem *base,
	uint32_t mas_index, uint8_t qmode_sel,
	struct msm_bus_bimc_qos_mode *qmode)
{
	uint32_t reg_val, val;

	switch (qmode_sel) {
	case BIMC_QOS_MODE_FIXED:
		reg_val = readl_relaxed(M_PRIOLVL_OVERRIDE_ADDR(
			base, mas_index)) & M_PRIOLVL_OVERRIDE_RMSK;
		val =  qmode->fixed.prio_level <<
			M_PRIOLVL_OVERRIDE_SHFT;
		writel_relaxed(((reg_val &
			~(M_PRIOLVL_OVERRIDE_BMSK)) | (val
			& M_PRIOLVL_OVERRIDE_BMSK)),
			M_PRIOLVL_OVERRIDE_ADDR(base, mas_index));

		reg_val = readl_relaxed(M_RD_CMD_OVERRIDE_ADDR(
			base, mas_index)) & M_RD_CMD_OVERRIDE_RMSK;
		val =  qmode->fixed.areq_prio_rd <<
			M_RD_CMD_OVERRIDE_AREQPRIO_SHFT;
		writel_relaxed(((reg_val & ~(M_RD_CMD_OVERRIDE_AREQPRIO_BMSK))
			| (val & M_RD_CMD_OVERRIDE_AREQPRIO_BMSK)),
			M_RD_CMD_OVERRIDE_ADDR(base, mas_index));

		reg_val = readl_relaxed(M_WR_CMD_OVERRIDE_ADDR(
			base, mas_index)) & M_WR_CMD_OVERRIDE_RMSK;
		val =  qmode->fixed.areq_prio_wr <<
			M_WR_CMD_OVERRIDE_AREQPRIO_SHFT;
		writel_relaxed(((reg_val & ~(M_WR_CMD_OVERRIDE_AREQPRIO_BMSK))
			| (val & M_WR_CMD_OVERRIDE_AREQPRIO_BMSK)),
			M_WR_CMD_OVERRIDE_ADDR(base, mas_index));
		/* Ensure that fixed mode register writes go through
		 * before returning
		 */
		wmb();
		break;

	case BIMC_QOS_MODE_REGULATOR:
	case BIMC_QOS_MODE_LIMITER:
		set_qos_prio_rl(M_BKE_HEALTH_3_CONFIG_ADDR(base,
			mas_index), M_BKE_HEALTH_3_CONFIG_RMSK, 3, qmode);
		set_qos_prio_rl(M_BKE_HEALTH_2_CONFIG_ADDR(base,
			mas_index), M_BKE_HEALTH_2_CONFIG_RMSK, 2, qmode);
		set_qos_prio_rl(M_BKE_HEALTH_1_CONFIG_ADDR(base,
			mas_index), M_BKE_HEALTH_1_CONFIG_RMSK, 1, qmode);
		set_qos_prio_rl(M_BKE_HEALTH_0_CONFIG_ADDR(base,
			mas_index), M_BKE_HEALTH_0_CONFIG_RMSK, 0 , qmode);
		break;
	case BIMC_QOS_MODE_BYPASS:
	default:
		break;
	}
}

static void set_qos_bw_regs(void __iomem *baddr, uint32_t mas_index,
	int32_t th, int32_t tm, int32_t tl, uint32_t gp,
	uint32_t gc)
{
	int32_t reg_val, val;
	int32_t bke_reg_val;
	int16_t val2;

	/* Disable BKE before writing to registers as per spec */
	bke_reg_val = readl_relaxed(M_BKE_EN_ADDR(baddr, mas_index));
	writel_relaxed((bke_reg_val & ~(M_BKE_EN_EN_BMSK)),
		M_BKE_EN_ADDR(baddr, mas_index));

	/* Write values of registers calculated */
	reg_val = readl_relaxed(M_BKE_GP_ADDR(baddr, mas_index))
		& M_BKE_GP_RMSK;
	val =  gp << M_BKE_GP_GP_SHFT;
	writel_relaxed(((reg_val & ~(M_BKE_GP_GP_BMSK)) | (val &
		M_BKE_GP_GP_BMSK)), M_BKE_GP_ADDR(baddr, mas_index));

	reg_val = readl_relaxed(M_BKE_GC_ADDR(baddr, mas_index)) &
		M_BKE_GC_RMSK;
	val =  gc << M_BKE_GC_GC_SHFT;
	writel_relaxed(((reg_val & ~(M_BKE_GC_GC_BMSK)) | (val &
		M_BKE_GC_GC_BMSK)), M_BKE_GC_ADDR(baddr, mas_index));

	reg_val = readl_relaxed(M_BKE_THH_ADDR(baddr, mas_index)) &
		M_BKE_THH_RMSK;
	val =  th << M_BKE_THH_THRESH_SHFT;
	writel_relaxed(((reg_val & ~(M_BKE_THH_THRESH_BMSK)) | (val &
		M_BKE_THH_THRESH_BMSK)), M_BKE_THH_ADDR(baddr, mas_index));

	reg_val = readl_relaxed(M_BKE_THM_ADDR(baddr, mas_index)) &
		M_BKE_THM_RMSK;
	val2 =	tm << M_BKE_THM_THRESH_SHFT;
	writel_relaxed(((reg_val & ~(M_BKE_THM_THRESH_BMSK)) | (val2 &
		M_BKE_THM_THRESH_BMSK)), M_BKE_THM_ADDR(baddr, mas_index));

	reg_val = readl_relaxed(M_BKE_THL_ADDR(baddr, mas_index)) &
		M_BKE_THL_RMSK;
	val2 =	tl << M_BKE_THL_THRESH_SHFT;
	writel_relaxed(((reg_val & ~(M_BKE_THL_THRESH_BMSK)) |
		(val2 & M_BKE_THL_THRESH_BMSK)), M_BKE_THL_ADDR(baddr,
		mas_index));

	/* Ensure that all bandwidth register writes have completed
	 * before returning
	 */
	wmb();
}

static void msm_bus_bimc_set_qos_bw(void __iomem *base, uint32_t qos_freq,
	uint32_t mas_index, struct msm_bus_bimc_qos_bw *qbw)
{
	uint32_t bke_en;

	/* Validate QOS Frequency */
	if (qos_freq == 0) {
		MSM_BUS_DBG("Zero frequency\n");
		return;
	}

	/* Get enable bit for BKE before programming the period */
	bke_en = (readl_relaxed(M_BKE_EN_ADDR(base, mas_index)) &
		M_BKE_EN_EN_BMSK) >> M_BKE_EN_EN_SHFT;

	/* Only calculate if there's a requested bandwidth and window */
	if (qbw->bw && qbw->ws) {
		int64_t th, tm, tl;
		uint32_t gp, gc;
		int64_t gp_nominal, gp_required, gp_calc, data, temp;
		int64_t win = qbw->ws * qos_freq;
		temp = win;
		/*
		 * Calculate nominal grant period defined by requested
		 * window size.
		 * Ceil this value to max grant period.
		 */
		bimc_div(&temp, 1000000);
		gp_nominal = min_t(uint64_t, MAX_GRANT_PERIOD, temp);
		/*
		 * Calculate max window size, defined by bw request.
		 * Units: (KHz, MB/s)
		 */
		gp_calc = MAX_GC * qos_freq * 1000;
		gp_required = gp_calc;
		bimc_div(&gp_required, qbw->bw);

		/* User min of two grant periods */
		gp = min_t(int64_t, gp_nominal, gp_required);

		/* Calculate bandwith in grants and ceil. */
		temp = qbw->bw * gp;
		data = qos_freq * 1000;
		bimc_div(&temp, data);
		gc = min_t(int64_t, MAX_GC, temp);

		/* Calculate thresholds */
		th = qbw->bw - qbw->thh;
		tm = qbw->bw - qbw->thm;
		tl = qbw->bw - qbw->thl;

		th = th * gp;
		bimc_div(&th, data);
		tm = tm * gp;
		bimc_div(&tm, data);
		tl = tl * gp;
		bimc_div(&tl, data);

		MSM_BUS_DBG("BIMC: BW: mas_index: %d, th: %llu tm: %llu\n",
			mas_index, th, tm);
		MSM_BUS_DBG("BIMC: tl: %llu gp:%u gc: %u bke_en: %u\n",
			tl, gp, gc, bke_en);
		set_qos_bw_regs(base, mas_index, th, tm, tl, gp, gc);
	} else
		/* Clear bandwidth registers */
		set_qos_bw_regs(base, mas_index, 0, 0, 0, 0, 0);
}

static int msm_bus_bimc_allocate_commit_data(struct msm_bus_fabric_registration
	*fab_pdata, void **cdata, int ctx)
{
	struct msm_bus_bimc_commit **cd = (struct msm_bus_bimc_commit **)cdata;
	struct msm_bus_bimc_info *binfo =
		(struct msm_bus_bimc_info *)fab_pdata->hw_data;

	MSM_BUS_DBG("Allocating BIMC commit data\n");
	*cd = kzalloc(sizeof(struct msm_bus_bimc_commit), GFP_KERNEL);
	if (!*cd) {
		MSM_BUS_DBG("Couldn't alloc mem for cdata\n");
		return -ENOMEM;
	}

	(*cd)->mas = binfo->cdata[ctx].mas;
	(*cd)->slv = binfo->cdata[ctx].slv;

	return 0;
}

static void *msm_bus_bimc_allocate_bimc_data(struct platform_device *pdev,
	struct msm_bus_fabric_registration *fab_pdata)
{
	struct resource *bimc_mem;
	struct resource *bimc_io;
	struct msm_bus_bimc_info *binfo;
	int i;

	MSM_BUS_DBG("Allocating BIMC data\n");
	binfo = kzalloc(sizeof(struct msm_bus_bimc_info), GFP_KERNEL);
	if (!binfo) {
		WARN(!binfo, "Couldn't alloc mem for bimc_info\n");
		return NULL;
	}

	binfo->qos_freq = fab_pdata->qos_freq;

	binfo->params.nmasters = fab_pdata->nmasters;
	binfo->params.nslaves = fab_pdata->nslaves;
	binfo->params.bus_id = fab_pdata->id;

	for (i = 0; i < NUM_CTX; i++) {
		binfo->cdata[i].mas = kzalloc(sizeof(struct
			msm_bus_node_hw_info) * fab_pdata->nmasters * 2,
			GFP_KERNEL);
		if (!binfo->cdata[i].mas) {
			MSM_BUS_ERR("Couldn't alloc mem for bimc master hw\n");
			kfree(binfo);
			return NULL;
		}

		binfo->cdata[i].slv = kzalloc(sizeof(struct
			msm_bus_node_hw_info) * fab_pdata->nslaves * 2,
			GFP_KERNEL);
		if (!binfo->cdata[i].slv) {
			MSM_BUS_DBG("Couldn't alloc mem for bimc slave hw\n");
			kfree(binfo->cdata[i].mas);
			kfree(binfo);
			return NULL;
		}
	}

	if (fab_pdata->virt) {
		MSM_BUS_DBG("Don't get memory regions for virtual fabric\n");
		goto skip_mem;
	}

	bimc_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!bimc_mem) {
		MSM_BUS_ERR("Cannot get BIMC Base address\n");
		kfree(binfo);
		return NULL;
	}

	bimc_io = request_mem_region(bimc_mem->start,
			resource_size(bimc_mem), pdev->name);
	if (!bimc_io) {
		MSM_BUS_ERR("BIMC memory unavailable\n");
		kfree(binfo);
		return NULL;
	}

	binfo->base = ioremap(bimc_mem->start, resource_size(bimc_mem));
	if (!binfo->base) {
		MSM_BUS_ERR("IOremap failed for BIMC!\n");
		release_mem_region(bimc_mem->start, resource_size(bimc_mem));
		kfree(binfo);
		return NULL;
	}

skip_mem:
	fab_pdata->hw_data = (void *)binfo;
	return (void *)binfo;
}

static void free_commit_data(void *cdata)
{
	struct msm_bus_bimc_commit *cd = (struct msm_bus_bimc_commit *)cdata;

	kfree(cd->mas);
	kfree(cd->slv);
	kfree(cd);
}

static void bke_switch(
	void __iomem *baddr, uint32_t mas_index, bool req, int mode)
{
	uint32_t reg_val, val, cur_val;

	val = req << M_BKE_EN_EN_SHFT;
	reg_val = readl_relaxed(M_BKE_EN_ADDR(baddr, mas_index));
	cur_val = reg_val & M_BKE_EN_RMSK;
	if (val == cur_val)
		return;

	if (!req && mode == BIMC_QOS_MODE_FIXED)
		set_qos_mode(baddr, mas_index, 1, 1, 1);

	writel_relaxed(((reg_val & ~(M_BKE_EN_EN_BMSK)) | (val &
		M_BKE_EN_EN_BMSK)), M_BKE_EN_ADDR(baddr, mas_index));
	/* Make sure BKE on/off goes through before changing priorities */
	wmb();

	if (req)
		set_qos_mode(baddr, mas_index, 0, 0, 0);
}

static void bimc_set_static_qos_bw(void __iomem *base, unsigned int qos_freq,
	int mport, struct msm_bus_bimc_qos_bw *qbw)
{
	int32_t bw_mbps, thh = 0, thm, thl, gc;
	int32_t gp;
	u64 temp;

	if (qos_freq == 0) {
		MSM_BUS_DBG("No QoS Frequency.\n");
		return;
	}

	if (!(qbw->bw && qbw->gp)) {
		MSM_BUS_DBG("No QoS Bandwidth or Window size\n");
		return;
	}

	/* Convert bandwidth to MBPS */
	temp = qbw->bw;
	bimc_div(&temp, 1000000);
	bw_mbps = temp;

	/* Grant period in clock cycles
	 * Grant period from bandwidth structure
	 * is in nano seconds, QoS freq is in KHz.
	 * Divide by 1000 to get clock cycles.
	 */
	gp = (qos_freq * qbw->gp) / (1000 * NSEC_PER_USEC);

	/* Grant count = BW in MBps * Grant period
	 * in micro seconds
	 */
	gc = bw_mbps * (qbw->gp / NSEC_PER_USEC);
	gc = min(gc, MAX_GC);

	/* Medium threshold = -((Medium Threshold percentage *
	 * Grant count) / 100)
	 */
	thm = -((qbw->thmp * gc) / 100);
	qbw->thm = thm;

	/* Low threshold = -(Grant count) */
	thl = -gc;
	qbw->thl = thl;

	MSM_BUS_DBG("%s: BKE parameters: gp %d, gc %d, thm %d thl %d thh %d",
			__func__, gp, gc, thm, thl, thh);

	trace_bus_bke_params(gc, gp, thl, thm, thl);
	set_qos_bw_regs(base, mport, thh, thm, thl, gp, gc);
}

static void msm_bus_bimc_config_master(
	struct msm_bus_fabric_registration *fab_pdata,
	struct msm_bus_inode_info *info,
	uint64_t req_clk, uint64_t req_bw)
{
	int mode, i, ports;
	struct msm_bus_bimc_info *binfo;
	uint64_t bw = 0;

	binfo = (struct msm_bus_bimc_info *)fab_pdata->hw_data;
	ports = info->node_info->num_mports;

	/**
	 * Here check the details of dual configuration.
	 * Take actions based on different modes.
	 * Check for threshold if limiter mode, etc.
	*/

	if (req_clk <= info->node_info->th[0]) {
		mode = info->node_info->mode;
		bw = info->node_info->bimc_bw[0];
	} else if ((info->node_info->num_thresh > 1) &&
			(req_clk <= info->node_info->th[1])) {
		mode = info->node_info->mode;
		bw = info->node_info->bimc_bw[1];
	} else
		mode = info->node_info->mode_thresh;

	switch (mode) {
	case BIMC_QOS_MODE_BYPASS:
	case BIMC_QOS_MODE_FIXED:
		for (i = 0; i < ports; i++)
			bke_switch(binfo->base, info->node_info->qport[i],
				BKE_OFF, mode);
		break;
	case BIMC_QOS_MODE_REGULATOR:
	case BIMC_QOS_MODE_LIMITER:
		for (i = 0; i < ports; i++) {
			/* If not in fixed mode, update bandwidth */
			if ((info->node_info->cur_lim_bw != bw)
					&& (mode != BIMC_QOS_MODE_FIXED)) {
				struct msm_bus_bimc_qos_bw qbw;
				qbw.ws = info->node_info->ws;
				qbw.bw = bw;
				qbw.gp = info->node_info->bimc_gp;
				qbw.thmp = info->node_info->bimc_thmp;
				bimc_set_static_qos_bw(binfo->base,
					binfo->qos_freq,
					info->node_info->qport[i], &qbw);
				info->node_info->cur_lim_bw = bw;
				MSM_BUS_DBG("%s: Qos is %d reqclk %llu bw %llu",
						__func__, mode, req_clk, bw);
			}
			bke_switch(binfo->base, info->node_info->qport[i],
				BKE_ON, mode);
		}
		break;
	default:
		break;
	}
}

static void msm_bus_bimc_update_bw(struct msm_bus_inode_info *hop,
	struct msm_bus_inode_info *info,
	struct msm_bus_fabric_registration *fab_pdata,
	void *sel_cdata, int *master_tiers,
	int64_t add_bw)
{
	struct msm_bus_bimc_info *binfo;
	struct msm_bus_bimc_qos_bw qbw;
	int i;
	int64_t bw;
	int ports = info->node_info->num_mports;
	struct msm_bus_bimc_commit *sel_cd =
		(struct msm_bus_bimc_commit *)sel_cdata;

	MSM_BUS_DBG("BIMC: Update bw for ID %d, with IID: %d: %lld\n",
		info->node_info->id, info->node_info->priv_id, add_bw);

	binfo = (struct msm_bus_bimc_info *)fab_pdata->hw_data;

	if (info->node_info->num_mports == 0) {
		MSM_BUS_DBG("BIMC: Skip Master BW\n");
		goto skip_mas_bw;
	}

	ports = info->node_info->num_mports;
	bw = INTERLEAVED_BW(fab_pdata, add_bw, ports);

	for (i = 0; i < ports; i++) {
		sel_cd->mas[info->node_info->masterp[i]].bw += bw;
		sel_cd->mas[info->node_info->masterp[i]].hw_id =
			info->node_info->mas_hw_id;
		MSM_BUS_DBG("BIMC: Update mas_bw for ID: %d -> %llu\n",
			info->node_info->priv_id,
			sel_cd->mas[info->node_info->masterp[i]].bw);
		if (info->node_info->hw_sel == MSM_BUS_RPM)
			sel_cd->mas[info->node_info->masterp[i]].dirty = 1;
		else {
			if (!info->node_info->qport) {
				MSM_BUS_DBG("No qos ports to update!\n");
				break;
			}
			MSM_BUS_DBG("qport: %d\n", info->node_info->qport[i]);
			qbw.bw = sel_cd->mas[info->node_info->masterp[i]].bw;
			qbw.ws = info->node_info->ws;
			/* Threshold low = 90% of bw */
			qbw.thl = div_s64((90 * bw), 100);
			/* Threshold medium = bw */
			qbw.thm = bw;
			/* Threshold high = 10% more than bw */
			qbw.thh = div_s64((110 * bw), 100);
			/* Check if info is a shared master.
			 * If it is, mark it dirty
			 * If it isn't, then set QOS Bandwidth.
			 * Also if dual-conf is set, don't program bw regs.
			 **/
			if (!info->node_info->dual_conf &&
			((info->node_info->mode == BIMC_QOS_MODE_LIMITER) ||
			(info->node_info->mode == BIMC_QOS_MODE_REGULATOR)))
				msm_bus_bimc_set_qos_bw(binfo->base,
					binfo->qos_freq,
					info->node_info->qport[i], &qbw);
		}
	}

skip_mas_bw:
	ports = hop->node_info->num_sports;
	MSM_BUS_DBG("BIMC: ID: %d, Sports: %d\n", hop->node_info->priv_id,
		ports);

	for (i = 0; i < ports; i++) {
		sel_cd->slv[hop->node_info->slavep[i]].bw += add_bw;
		sel_cd->slv[hop->node_info->slavep[i]].hw_id =
			hop->node_info->slv_hw_id;
		MSM_BUS_DBG("BIMC: Update slave_bw: ID: %d -> %llu\n",
			hop->node_info->priv_id,
			sel_cd->slv[hop->node_info->slavep[i]].bw);
		MSM_BUS_DBG("BIMC: Update slave_bw: index: %d\n",
			hop->node_info->slavep[i]);
		/* Check if hop is a shared slave.
		 * If it is, mark it dirty
		 * If it isn't, then nothing to be done as the
		 * slaves are in bypass mode.
		 **/
		if (hop->node_info->hw_sel == MSM_BUS_RPM) {
			MSM_BUS_DBG("Slave dirty: %d, slavep: %d\n",
				hop->node_info->priv_id,
				hop->node_info->slavep[i]);
			sel_cd->slv[hop->node_info->slavep[i]].dirty = 1;
		}
	}
}

static int msm_bus_bimc_commit(struct msm_bus_fabric_registration
	*fab_pdata, void *hw_data, void **cdata)
{
	MSM_BUS_DBG("\nReached BIMC Commit\n");
	msm_bus_remote_hw_commit(fab_pdata, hw_data, cdata);
	return 0;
}

static void msm_bus_bimc_config_limiter(
	struct msm_bus_fabric_registration *fab_pdata,
	struct msm_bus_inode_info *info)
{
	struct msm_bus_bimc_info *binfo;
	int mode, i, ports;

	binfo = (struct msm_bus_bimc_info *)fab_pdata->hw_data;
	ports = info->node_info->num_mports;

	if (!info->node_info->qport) {
		MSM_BUS_DBG("No QoS Ports to init\n");
		return;
	}

	if (info->cur_lim_bw)
		mode = BIMC_QOS_MODE_LIMITER;
	else
		mode = info->node_info->mode;

	switch (mode) {
	case BIMC_QOS_MODE_BYPASS:
	case BIMC_QOS_MODE_FIXED:
		for (i = 0; i < ports; i++)
			bke_switch(binfo->base, info->node_info->qport[i],
				BKE_OFF, mode);
		break;
	case BIMC_QOS_MODE_REGULATOR:
	case BIMC_QOS_MODE_LIMITER:
		if (info->cur_lim_bw != info->cur_prg_bw) {
			MSM_BUS_DBG("Enabled BKE throttling node %d to %llu\n",
				info->node_info->id, info->cur_lim_bw);
			trace_bus_bimc_config_limiter(info->node_info->id,
				info->cur_lim_bw);
			for (i = 0; i < ports; i++) {
				/* If not in fixed mode, update bandwidth */
				struct msm_bus_bimc_qos_bw qbw;

				qbw.ws = info->node_info->ws;
				qbw.bw = info->cur_lim_bw;
				qbw.gp = info->node_info->bimc_gp;
				qbw.thmp = info->node_info->bimc_thmp;
				bimc_set_static_qos_bw(binfo->base,
					binfo->qos_freq,
					info->node_info->qport[i], &qbw);
				bke_switch(binfo->base,
					info->node_info->qport[i],
					BKE_ON, mode);
				info->cur_prg_bw = qbw.bw;
			}
		}
		break;
	default:
		break;
	}
}

static void bimc_init_mas_reg(struct msm_bus_bimc_info *binfo,
	struct msm_bus_inode_info *info,
	struct msm_bus_bimc_qos_mode *qmode, int mode)
{
	int i;

	switch (mode) {
	case BIMC_QOS_MODE_FIXED:
		qmode->fixed.prio_level = info->node_info->prio_lvl;
		qmode->fixed.areq_prio_rd = info->node_info->prio_rd;
		qmode->fixed.areq_prio_wr = info->node_info->prio_wr;
		break;
	case BIMC_QOS_MODE_LIMITER:
		qmode->rl.qhealth[0].limit_commands = 1;
		qmode->rl.qhealth[1].limit_commands = 0;
		qmode->rl.qhealth[2].limit_commands = 0;
		qmode->rl.qhealth[3].limit_commands = 0;
		break;
	default:
		break;
	}

	if (!info->node_info->qport) {
		MSM_BUS_DBG("No QoS Ports to init\n");
		return;
	}

	for (i = 0; i < info->node_info->num_mports; i++) {
		/* If not in bypass mode, update priority */
		if (mode != BIMC_QOS_MODE_BYPASS) {
			msm_bus_bimc_set_qos_prio(binfo->base,
				info->node_info->
				qport[i], mode, qmode);

			/* If not in fixed mode, update bandwidth */
			if (mode != BIMC_QOS_MODE_FIXED) {
				struct msm_bus_bimc_qos_bw qbw;
				qbw.ws = info->node_info->ws;
				qbw.bw = info->node_info->bimc_bw[0];
				qbw.gp = info->node_info->bimc_gp;
				qbw.thmp = info->node_info->bimc_thmp;
				bimc_set_static_qos_bw(binfo->base,
					binfo->qos_freq,
					info->node_info->qport[i], &qbw);
			}
		}

		/* set mode */
		msm_bus_bimc_set_qos_mode(binfo->base,
					info->node_info->qport[i],
					mode);
	}
}

static void init_health_regs(struct msm_bus_bimc_info *binfo,
				struct msm_bus_inode_info *info,
				struct msm_bus_bimc_qos_mode *qmode,
				int mode)
{
	int i;

	if (mode == BIMC_QOS_MODE_LIMITER) {
		qmode->rl.qhealth[0].limit_commands = 1;
		qmode->rl.qhealth[1].limit_commands = 0;
		qmode->rl.qhealth[2].limit_commands = 0;
		qmode->rl.qhealth[3].limit_commands = 0;

		if (!info->node_info->qport) {
			MSM_BUS_DBG("No QoS Ports to init\n");
			return;
		}

		for (i = 0; i < info->node_info->num_mports; i++) {
			/* If not in bypass mode, update priority */
			if (mode != BIMC_QOS_MODE_BYPASS)
				msm_bus_bimc_set_qos_prio(binfo->base,
				info->node_info->qport[i], mode, qmode);
		}
	}
}


static int msm_bus_bimc_mas_init(struct msm_bus_bimc_info *binfo,
	struct msm_bus_inode_info *info)
{
	struct msm_bus_bimc_qos_mode *qmode;
	qmode = kzalloc(sizeof(struct msm_bus_bimc_qos_mode),
		GFP_KERNEL);
	if (!qmode) {
		MSM_BUS_WARN("Couldn't alloc prio data for node: %d\n",
			info->node_info->id);
		return -ENOMEM;
	}

	info->hw_data = (void *)qmode;

	/**
	 * If the master supports dual configuration,
	 * configure registers for both modes
	 */
	if (info->node_info->dual_conf)
		bimc_init_mas_reg(binfo, info, qmode,
			info->node_info->mode_thresh);
	else if (info->node_info->nr_lim)
		init_health_regs(binfo, info, qmode, BIMC_QOS_MODE_LIMITER);

	bimc_init_mas_reg(binfo, info, qmode, info->node_info->mode);
	return 0;
}

static void msm_bus_bimc_node_init(void *hw_data,
	struct msm_bus_inode_info *info)
{
	struct msm_bus_bimc_info *binfo =
		(struct msm_bus_bimc_info *)hw_data;

	if (!IS_SLAVE(info->node_info->priv_id) &&
		(info->node_info->hw_sel != MSM_BUS_RPM))
		msm_bus_bimc_mas_init(binfo, info);
}

static int msm_bus_bimc_port_halt(uint32_t haltid, uint8_t mport)
{
	return 0;
}

static int msm_bus_bimc_port_unhalt(uint32_t haltid, uint8_t mport)
{
	return 0;
}

static int msm_bus_bimc_limit_mport(struct msm_bus_node_device_type *info,
				void __iomem *qos_base, uint32_t qos_off,
				uint32_t qos_delta, uint32_t qos_freq,
				bool enable_lim, u64 lim_bw)
{
	int mode;
	int i;

	if (ZERO_OR_NULL_PTR(info->node_info->qport)) {
		MSM_BUS_DBG("No QoS Ports to limit\n");
		return 0;
	}

	if (enable_lim && lim_bw) {
		mode =  BIMC_QOS_MODE_LIMITER;

		if (!info->node_info->lim_bw) {
			struct msm_bus_bimc_qos_mode qmode;
			qmode.rl.qhealth[0].limit_commands = 1;
			qmode.rl.qhealth[1].limit_commands = 0;
			qmode.rl.qhealth[2].limit_commands = 0;
			qmode.rl.qhealth[3].limit_commands = 0;

			for (i = 0; i < info->node_info->num_qports; i++) {
				/* If not in bypass mode, update priority */
				if (mode != BIMC_QOS_MODE_BYPASS)
					msm_bus_bimc_set_qos_prio(qos_base,
					info->node_info->qport[i], mode,
					&qmode);
			}
		}

		for (i = 0; i < info->node_info->num_qports; i++) {
			struct msm_bus_bimc_qos_bw qbw;
			/* If not in fixed mode, update bandwidth */
			if ((info->node_info->lim_bw != lim_bw)) {
				qbw.ws = info->node_info->qos_params.ws;
				qbw.bw = lim_bw;
				qbw.gp = info->node_info->qos_params.gp;
				qbw.thmp = info->node_info->qos_params.thmp;
				bimc_set_static_qos_bw(qos_base, qos_freq,
					info->node_info->qport[i], &qbw);
			}
			bke_switch(qos_base, info->node_info->qport[i],
				BKE_ON, mode);
		}
		info->node_info->lim_bw = lim_bw;
	} else {
		mode = info->node_info->qos_params.mode;
		for (i = 0; i < info->node_info->num_qports; i++) {
			bke_switch(qos_base, info->node_info->qport[i],
				BKE_OFF, mode);
		}
	}
	info->node_info->qos_params.cur_mode = mode;
	return 0;
}

static int msm_bus_bimc_qos_init(struct msm_bus_node_device_type *info,
				void __iomem *qos_base,
				uint32_t qos_off, uint32_t qos_delta,
				uint32_t qos_freq)
{
	int i;
	struct msm_bus_bimc_qos_mode qmode;

	switch (info->node_info->qos_params.mode) {
	case BIMC_QOS_MODE_FIXED:
		qmode.fixed.prio_level = info->node_info->qos_params.prio_lvl;
		qmode.fixed.areq_prio_rd = info->node_info->qos_params.prio_rd;
		qmode.fixed.areq_prio_wr = info->node_info->qos_params.prio_wr;
		break;
	case BIMC_QOS_MODE_LIMITER:
		qmode.rl.qhealth[0].limit_commands = 1;
		qmode.rl.qhealth[1].limit_commands = 0;
		qmode.rl.qhealth[2].limit_commands = 0;
		qmode.rl.qhealth[3].limit_commands = 0;
		break;
	default:
		break;
	}

	if (ZERO_OR_NULL_PTR(info->node_info->qport)) {
		MSM_BUS_DBG("No QoS Ports to init\n");
		return 0;
	}

	for (i = 0; i < info->node_info->num_qports; i++) {
		/* If not in bypass mode, update priority */
		if (info->node_info->qos_params.mode != BIMC_QOS_MODE_BYPASS)
			msm_bus_bimc_set_qos_prio(qos_base, info->node_info->
				qport[i], info->node_info->qos_params.mode,
									&qmode);

		/* set mode */
		if (info->node_info->qos_params.mode == BIMC_QOS_MODE_LIMITER)
			bke_switch(qos_base, info->node_info->qport[i],
				BKE_OFF, BIMC_QOS_MODE_FIXED);
		else
		       msm_bus_bimc_set_qos_mode(qos_base,
				info->node_info->qport[i],
				info->node_info->qos_params.mode);
	}

	return 0;
}

static int msm_bus_bimc_set_bw(struct msm_bus_node_device_type *dev,
				void __iomem *qos_base, uint32_t qos_off,
				uint32_t qos_delta, uint32_t qos_freq)
{
	struct msm_bus_bimc_qos_bw qbw;
	int i;
	int64_t bw = 0;
	int ret = 0;
	struct msm_bus_node_info_type *info = dev->node_info;

	if (info && info->num_qports &&
		((info->qos_params.mode == BIMC_QOS_MODE_LIMITER) ||
		(info->qos_params.mode == BIMC_QOS_MODE_REGULATOR))) {
		bw = msm_bus_div64(info->num_qports,
				dev->node_ab.ab[DUAL_CTX]);

		for (i = 0; i < info->num_qports; i++) {
			MSM_BUS_DBG("BIMC: Update mas_bw for ID: %d -> %llu\n",
				info->id, bw);

			if (!info->qport) {
				MSM_BUS_DBG("No qos ports to update!\n");
				break;
			}

			qbw.bw = bw + info->qos_params.bw_buffer;
			trace_bus_bimc_config_limiter(info->id, bw);

			/* Default to gp of 5us */
			qbw.gp = (info->qos_params.gp ?
					info->qos_params.gp : 5000);
			/* Default to thmp of 50% */
			qbw.thmp = (info->qos_params.thmp ?
					info->qos_params.thmp : 50);
			/*
			 * If the BW vote is 0 then set the QoS mode to
			 * Fixed.
			 */
			if (bw) {
				bimc_set_static_qos_bw(qos_base, qos_freq,
					info->qport[i], &qbw);
				bke_switch(qos_base, info->qport[i],
					BKE_ON, info->qos_params.mode);
			} else {
				bke_switch(qos_base, info->qport[i],
					BKE_OFF, BIMC_QOS_MODE_FIXED);
			}
		}
	}
	return ret;
}

int msm_bus_bimc_hw_init(struct msm_bus_fabric_registration *pdata,
	struct msm_bus_hw_algorithm *hw_algo)
{
	/* Set interleaving to true by default */
	MSM_BUS_DBG("\nInitializing BIMC...\n");
	pdata->il_flag = true;
	hw_algo->allocate_commit_data = msm_bus_bimc_allocate_commit_data;
	hw_algo->allocate_hw_data = msm_bus_bimc_allocate_bimc_data;
	hw_algo->node_init = msm_bus_bimc_node_init;
	hw_algo->free_commit_data = free_commit_data;
	hw_algo->update_bw = msm_bus_bimc_update_bw;
	hw_algo->commit = msm_bus_bimc_commit;
	hw_algo->port_halt = msm_bus_bimc_port_halt;
	hw_algo->port_unhalt = msm_bus_bimc_port_unhalt;
	hw_algo->config_master = msm_bus_bimc_config_master;
	hw_algo->config_limiter = msm_bus_bimc_config_limiter;
	/* BIMC slaves are shared. Slave registers are set through RPM */
	if (!pdata->ahb)
		pdata->rpm_enabled = 1;
	return 0;
}

int msm_bus_bimc_set_ops(struct msm_bus_node_device_type *bus_dev)
{
	if (!bus_dev)
		return -ENODEV;
	else {
		bus_dev->fabdev->noc_ops.qos_init = msm_bus_bimc_qos_init;
		bus_dev->fabdev->noc_ops.set_bw = msm_bus_bimc_set_bw;
		bus_dev->fabdev->noc_ops.limit_mport = msm_bus_bimc_limit_mport;
	}
	return 0;
}
EXPORT_SYMBOL(msm_bus_bimc_set_ops);
